Skip to main content
Strapi provides a complete media library for managing images, videos, documents, and other files. strapi2front generates typed helpers for working with media fields and uploading files.

Media Types

The generated StrapiMedia interface represents files in Strapi’s media library:
utils.ts
export interface StrapiMedia {
  id: number;
  documentId: string;  // v5 only
  name: string;
  alternativeText: string | null;
  caption: string | null;
  width: number;  // For images
  height: number;  // For images
  formats: {
    thumbnail?: StrapiMediaFormat;
    small?: StrapiMediaFormat;
    medium?: StrapiMediaFormat;
    large?: StrapiMediaFormat;
  } | null;
  hash: string;
  ext: string;  // .jpg, .png, .pdf, etc.
  mime: string;  // image/jpeg, application/pdf, etc.
  size: number;  // File size in KB
  url: string;  // Full URL to the file
  previewUrl: string | null;
  provider: string;  // local, cloudinary, s3, etc.
  createdAt: string;
  updatedAt: string;
}

export interface StrapiMediaFormat {
  name: string;
  hash: string;
  ext: string;
  mime: string;
  width: number;
  height: number;
  size: number;
  url: string;
}

Single vs Multiple Media Fields

Strapi media fields can be single or multiple:
article.ts
import type { StrapiMedia } from '../utils';

export interface Article extends StrapiBaseEntity {
  title: string;
  
  // Single media field (nullable)
  coverImage: StrapiMedia | null;
  
  // Multiple media field (array)
  gallery: StrapiMedia[];
}

Displaying Media

Display Images

---
import type { Article } from '@/strapi/collections/article/types';

interface Props {
  article: Article;
}

const { article } = Astro.props;
---

{article.coverImage && (
  <img 
    src={article.coverImage.url} 
    alt={article.coverImage.alternativeText || article.title}
    width={article.coverImage.width}
    height={article.coverImage.height}
  />
)}

Image Formats

Strapi automatically generates multiple sizes for images:
Small preview (typically 156x156 or 245x156)
const thumb = image.formats?.thumbnail?.url;
Not all formats are generated for every image. Small images may only have a thumbnail or no formats at all. Always check if a format exists before using it.

Display Non-Image Files

Document Download
---
const { document } = Astro.props;
---

{document && (
  <a href={document.url} download>
    <span>Download {document.name}</span>
    <span class="size">{(document.size / 1024).toFixed(2)} MB</span>
    <span class="type">{document.ext}</span>
  </a>
)}

Uploading Files

strapi2front generates file upload helpers via the Strapi client.

Upload Process

File uploads in Strapi are a two-step process:
1

Upload the file

Upload file to Strapi’s media library via the upload API
2

Link to content

Use the returned file ID to link it to your content type

Generated Upload Helpers

The client includes typed file upload methods:
client.ts
export interface StrapiFileInfo {
  name?: string;
  alternativeText?: string;
  caption?: string;
}

export const files = {
  /**
   * Upload a file to Strapi media library
   */
  async upload(file: File | Blob, options?: { fileInfo?: StrapiFileInfo }): Promise<StrapiMedia> {
    const response = await strapiClient.files.upload(file, options);
    return response;
  },
  
  /**
   * Find files in media library
   */
  async find(params?: Record<string, unknown>): Promise<StrapiMedia[]> {
    const response = await strapiClient.files.find(params);
    return Array.isArray(response) ? response : [];
  },
  
  /**
   * Find one file by ID
   */
  async findOne(fileId: number): Promise<StrapiMedia> {
    const response = await strapiClient.files.findOne(fileId);
    return response;
  },
  
  /**
   * Update file metadata
   */
  async update(fileId: number, fileInfo: StrapiFileInfo): Promise<StrapiMedia> {
    const response = await strapiClient.files.update(fileId, fileInfo);
    return response;
  },
  
  /**
   * Delete a file
   */
  async delete(fileId: number): Promise<StrapiMedia> {
    const response = await strapiClient.files.delete(fileId);
    return response;
  },
};

Upload Examples

import { files } from '@/strapi/client';

async function uploadImage(file: File) {
  // Upload file to media library
  const uploadedFile = await files.upload(file, {
    fileInfo: {
      name: 'My Image',
      alternativeText: 'Description of the image',
      caption: 'Photo caption',
    },
  });
  
  console.log('Uploaded:', uploadedFile);
  console.log('File ID:', uploadedFile.id);
  console.log('File URL:', uploadedFile.url);
  
  return uploadedFile;
}

Upload in Forms

Browser Form Upload

components/ArticleForm.astro
<form id="article-form">
  <div>
    <label for="title">Title</label>
    <input type="text" id="title" name="title" required />
  </div>
  
  <div>
    <label for="cover">Cover Image</label>
    <input type="file" id="cover" name="cover" accept="image/*" />
  </div>
  
  <div>
    <label for="gallery">Gallery (Multiple)</label>
    <input type="file" id="gallery" name="gallery" accept="image/*" multiple />
  </div>
  
  <button type="submit">Create Article</button>
</form>

<script>
  import { files } from '@/strapi/client';
  import { articleService } from '@/strapi/collections/article/service';
  
  const form = document.getElementById('article-form') as HTMLFormElement;
  
  form.addEventListener('submit', async (e) => {
    e.preventDefault();
    
    const formData = new FormData(form);
    const title = formData.get('title') as string;
    const coverFile = formData.get('cover') as File;
    const galleryFiles = formData.getAll('gallery') as File[];
    
    try {
      // Upload cover image
      let coverId = null;
      if (coverFile && coverFile.size > 0) {
        const uploadedCover = await files.upload(coverFile, {
          fileInfo: { alternativeText: title },
        });
        coverId = uploadedCover.id;
      }
      
      // Upload gallery images
      const galleryIds: number[] = [];
      for (const file of galleryFiles) {
        if (file.size > 0) {
          const uploaded = await files.upload(file);
          galleryIds.push(uploaded.id);
        }
      }
      
      // Create article with uploaded files
      const article = await articleService.create({
        title,
        slug: title.toLowerCase().replace(/\s+/g, '-'),
        coverImage: coverId,
        gallery: galleryIds,
      });
      
      console.log('Article created:', article);
      window.location.href = `/blog/${article.slug}`;
    } catch (error) {
      console.error('Upload failed:', error);
    }
  });
</script>

React Form Upload

components/UploadForm.tsx
import { useState } from 'react';
import { files } from '@/strapi/client';
import { articleService } from '@/strapi/collections/article/service';

export function UploadForm() {
  const [coverFile, setCoverFile] = useState<File | null>(null);
  const [uploading, setUploading] = useState(false);
  const [previewUrl, setPreviewUrl] = useState<string | null>(null);
  
  const handleFileChange = (e: React.ChangeEvent<HTMLInputElement>) => {
    const file = e.target.files?.[0];
    if (file) {
      setCoverFile(file);
      // Create preview
      const url = URL.createObjectURL(file);
      setPreviewUrl(url);
    }
  };
  
  const handleSubmit = async (e: React.FormEvent<HTMLFormElement>) => {
    e.preventDefault();
    setUploading(true);
    
    try {
      const formData = new FormData(e.currentTarget);
      const title = formData.get('title') as string;
      
      let coverId = null;
      if (coverFile) {
        const uploaded = await files.upload(coverFile, {
          fileInfo: {
            alternativeText: title,
            caption: 'Article cover image',
          },
        });
        coverId = uploaded.id;
      }
      
      const article = await articleService.create({
        title,
        slug: title.toLowerCase().replace(/\s+/g, '-'),
        coverImage: coverId,
      });
      
      console.log('Success:', article);
    } catch (error) {
      console.error('Error:', error);
    } finally {
      setUploading(false);
    }
  };
  
  return (
    <form onSubmit={handleSubmit}>
      <input type="text" name="title" required />
      
      <div>
        <input 
          type="file" 
          accept="image/*" 
          onChange={handleFileChange}
        />
        {previewUrl && (
          <img src={previewUrl} alt="Preview" width={200} />
        )}
      </div>
      
      <button type="submit" disabled={uploading}>
        {uploading ? 'Uploading...' : 'Create Article'}
      </button>
    </form>
  );
}

Update Media Fields

// Upload new image
const newImage = await files.upload(file);

// Replace existing cover image
await articleService.update('article-123', {
  coverImage: newImage.id,
});

File Validation

Validate files before upload:
utils/validation.ts
export function validateImage(file: File): { valid: boolean; error?: string } {
  // Check file type
  const allowedTypes = ['image/jpeg', 'image/png', 'image/webp', 'image/gif'];
  if (!allowedTypes.includes(file.type)) {
    return { valid: false, error: 'File must be an image (JPEG, PNG, WebP, or GIF)' };
  }
  
  // Check file size (max 5MB)
  const maxSize = 5 * 1024 * 1024;
  if (file.size > maxSize) {
    return { valid: false, error: 'File must be smaller than 5MB' };
  }
  
  return { valid: true };
}

// Usage
const validation = validateImage(file);
if (!validation.valid) {
  alert(validation.error);
  return;
}

const uploaded = await files.upload(file);

Optimize Images

Use Strapi’s generated formats for optimal performance:
components/OptimizedImage.astro
---
import type { StrapiMedia } from '@/strapi/utils';

interface Props {
  image: StrapiMedia;
  alt?: string;
  sizes?: string;
}

const { image, alt, sizes = '100vw' } = Astro.props;

// Build srcset from available formats
const srcset = [
  image.formats?.small && `${image.formats.small.url} ${image.formats.small.width}w`,
  image.formats?.medium && `${image.formats.medium.url} ${image.formats.medium.width}w`,
  image.formats?.large && `${image.formats.large.url} ${image.formats.large.width}w`,
  `${image.url} ${image.width}w`,
].filter(Boolean).join(', ');
---

<img
  src={image.formats?.medium?.url || image.url}
  srcset={srcset}
  sizes={sizes}
  alt={alt || image.alternativeText || ''}
  width={image.width}
  height={image.height}
  loading="lazy"
  decoding="async"
/>
Usage:
<OptimizedImage 
  image={article.coverImage} 
  alt={article.title}
  sizes="(max-width: 768px) 100vw, 50vw"
/>

Next Steps