Skip to content

File and Image Field Patterns

When to Use

When handling file uploads (documents, images, media) requiring file validation, storage organization, and derivative generation.

Decision

If you need... Use... Why
Generic file upload file field type Handles any MIME type with validation
Images with derivatives image field type Automatic image styles, metadata
File organization file_directory setting Token-based directory structure
Security validation file_extensions, max_filesize Prevent malicious uploads
Image metadata alt/title fields Accessibility and SEO

Pattern

Image field with security and performance settings:

BaseFieldDefinition::create('image')
  ->setLabel(t('Image'))
  ->setSetting('file_directory', 'images/[date:custom:Y]-[date:custom:m]')
  ->setSetting('file_extensions', 'png jpg jpeg webp')
  ->setSetting('max_filesize', '5 MB')
  ->setSetting('max_resolution', '3000x3000')
  ->setSetting('min_resolution', '400x300')
  ->setSetting('alt_field', TRUE)
  ->setSetting('alt_field_required', TRUE)
  ->setSetting('title_field', FALSE)
  ->setDisplayOptions('form', [
    'type' => 'image_image',
    'weight' => 5,
  ])
  ->setDisplayOptions('view', [
    'type' => 'image',
    'label' => 'hidden',
    'settings' => [
      'image_style' => 'large',
      'image_loading' => ['attribute' => 'lazy'],
    ],
  ]);

File field with upload validation:

BaseFieldDefinition::create('file')
  ->setLabel(t('Document'))
  ->setSetting('file_directory', 'documents/[node:created:custom:Y]')
  ->setSetting('file_extensions', 'pdf doc docx xls xlsx txt')
  ->setSetting('max_filesize', '10 MB')
  ->setSetting('description_field', TRUE);

Reference: /core/modules/file/src/Plugin/Field/FieldType/FileItem.php, /core/modules/image/src/Plugin/Field/FieldType/ImageItem.php

Common Mistakes

  • Wrong: Not restricting file_extensions → Right: CRITICAL security risk; allow .php uploads = RCE
  • Wrong: Missing max_filesize → Right: Disk space exhaustion, DoS attacks
  • Wrong: Not requiring alt_field → Right: Accessibility failure, WCAG violation
  • Wrong: Allowing dangerous extensions → Right: .exe, .js, .phtml, .svg (can contain JavaScript)
  • Wrong: Using public:// for sensitive files → Right: Files directly accessible; use private:// with access control
  • Wrong: Not setting file_directory → Right: All files in root; poor organization, slow filesystem operations
  • Wrong: Using original images in display → Right: Performance issue; always use image_style

Security (CRITICAL): - ALWAYS whitelist file_extensions. NEVER allow executable types. - Dangerous extensions: php, phtml, php3, php4, php5, phar, exe, js, html, htm, svg (can embed JS) - Safe extensions: png, jpg, jpeg, gif, webp, pdf, doc, docx, txt, csv, zip - Use private:// stream for sensitive files. Public files bypass access control. - Validate uploaded files in hook_file_validate(). Don't trust MIME types (client-provided). - Set max_filesize to prevent disk exhaustion DoS attacks.

Performance: - Use file_directory with date tokens to distribute files: [date:custom:Y]-[date:custom:m] - Prevents single directory with millions of files (filesystem performance degrades) - ALWAYS use image_style in formatters. Never display original images. - Set image_loading: lazy for below-fold images (improves LCP by ~30%) - Use WebP format for web delivery (image styles can auto-convert)

Accessibility: - ALWAYS require alt_field for images. It's not optional. - alt_field_required: TRUE is mandatory for WCAG 2.1 Level A compliance. - Provide helpful alt text guidance in field description.

See Also