Strapi is an open-source headless CMS,its inbuilt media library has support for simple uploads, but modern content platforms demand more, particularly when serving quick, responsive, and optimized images, videos, and audio across devices and networks. These traditional file uploads do not include solutions for tasks like transcoding, adaptive streaming, responsive images, codec fallback, and advanced audio handling. Plugins fills this gap, providing better performance, compatibility, and scalability without deep-end customization of the backend.
Media Managing Challenges
Devices and browsers accept different content types, such as MP4 (H.264) versus WebM (VP9) for video, WebP versus AVIF for images, MP3 versus OGG for audio. These affect load speed, bandwidth, and playback quality. Without optimization, users get slow loads, degraded media, and inconsistent experiences. Default upload plugin for Strapi saves files but misses:
- Automatic Transcoding for Several Formats
- Adaptive Bit-Rate Streaming
- Thumbnail and Poster Generation
- Image Size Reduction and Format Change
- Audio MIME-type Fallback and Streaming Support
Efficient embedding requires preparing media for the frontend, such as handling codecs, resolutions, responsive sizing, and fallbacks for compatibility.
Latest Media Standards
Image types such as AVIF and WebP have file sizes that are lightweight yet do not impair quality, which increases loading time and SEO. Video protocols such as HLS and DASH adapt quality based on user bandwidth. Audio types such as AAC and OGG compress well without losing sharpness. Plugins that are tightly integrated with service providers such as Cincopa or Thumbor compress, convert, manage breakpoints, and control CDN delivery on your behalf.
Example: Rendering Optimized Images with Thumbor via Strapi
// components/ResponsiveImage.js
import React from ‘react’; const ResponsiveImage = ({ imageUrl, alt }) => { const thumborBaseURL = ‘https://your-thumbor-instance.com’; // Replace with your Thumbor URL const encodedPath = encodeURIComponent(imageUrl); // Assuming Strapi provides the full media URL return ( <picture> {/* AVIF format */} <source srcSet={`${thumborBaseURL}/unsafe/800×0/filters:format(avif)/${encodedPath}`} type=”image/avif” /> {/* WebP format */} <source srcSet={`${thumborBaseURL}/unsafe/800×0/filters:format(webp)/${encodedPath}`} type=”image/webp” /> {/* Fallback (e.g., JPEG/PNG) */} <img src={`${thumborBaseURL}/unsafe/800×0/filters:format(jpeg)/${encodedPath}`} alt={alt} loading=”lazy” width=”800″ /> </picture> ); }; export default ResponsiveImage; |
Explanation:
- unsafe/800×0: Resizes image to 800px wide while maintaining aspect ratio.
- filters:format(…): Specifies the output format (avif, webp, or fallback jpeg).
- encodeURIComponent(imageUrl): Ensures special characters in URLs are correctly handled.
- <picture>: Provides format fallbacks for better compatibility and performance.
Embedding in Strapi
To embed video, images, and audio content in Strapi, frontend configuration and backend customization are required. Its default upload plugin only allows uploading files with few possibilities of transformation or embed-level manipulation. Developers have to extend this with Strapi plugins made for media.
Example:
// src/plugins/embed-field/strapi-server.js
module.exports = { register({ strapi }) { strapi.customFields.register({ name: ‘media-embed’, plugin: ’embed-field’, type: ‘string’, input: { // Admin UI config type: ‘text’, placeholder: ‘Paste YouTube/Vimeo URL or Strapi Media UID’, // Validation pattern: { message: ‘Must be a valid URL or Media UID’, regex: /^(https?:\/\/\S+|strapi::media-\w+)$/, }, }, }); }, }; |
Explanation:
- name is the identifier for this field (media-embed).
- plugin specifies which plugin owns this field (embed-field).
- type indicates the data type stored in the database (string).
For videos, create content types that store both local media and external URLs (such as YouTube, Vimeo, Cincopa), plus metadata like thumbnails and transcoding details.
Example:
// api/video/content-types/video/schema.json
{ “kind”: “collectionType”, “collectionName”: “videos”, “info”: { “name”: “Video”, “description”: “Embed videos with adaptive streaming” }, “options”: { “draftAndPublish”: true }, “attributes”: { “title”: { “type”: “string” }, “localVideo”: { “type”: “media”, “allowedTypes”: [“videos”] }, “externalUrl”: { “type”: “string” }, “thumbnail”: { “type”: “media”, “allowedTypes”: [“images”] }, “transcodingData”: { “type”: “json” } } } |
Explanation:
- localVideo is a media field for uploading video files directly to Strapi (allowed types are only videos).
- externalUrl is a string field for linking to a video hosted elsewhere (e.g., YouTube, Vimeo).
- thumbnail is a media field for uploading an image used as the video’s thumbnail (allowed types are only images).
- transcodingData is a JSON field for storing structured data, such as details from a video processing service (formats, resolutions, etc.).
Advanced Strapi video embedding can also involve platforms like Cincopa, with developers crafting custom plugins or REST APIs to ingest a video, obtain playback URLs, and store its metadata. It also offers thumbnails, subtitles, analytics, and adaptive bitrate streaming, which massively enhance performance.
Just like video, audio can be embedded by an external link or uploaded directly to the media library. Additionally, proper MIME types need to be exposed by APIs, and CORS headers configured properly for external streaming services, for embedding <audio> players in the frontend.
Example:
import React from ‘react’;
const VideoPlayer = ({ video }) => { if (video.externalUrl) { return ( <iframe src={video.externalUrl} title={video.title} allowFullScreen /> ); } return ( <video controls poster={video.thumbnail?.url}> <source src={video.localVideo?.url} type=”video/mp4″ /> {video.transcodingData?.hlsUrl && ( <source src={video.transcodingData.hlsUrl} type=”application/vnd.apple.mpegurl” /> )} </video> ); }; export default VideoPlayer; |
Explanation:
- src sets the video URL.
- allowFullScreen allows the user to watch the video in full-screen mode.
- The first <source> loads the video from video.localVideo.url in MP4 format.
- The second <source> is conditionally rendered only if video.transcodingData.hlsUrl exists.
To provide consistent embedding, the front end uses reusable elements that accept media metadata from Strapi APIs and display custom players for video, audio, or image galleries.
Key Considerations
- Offloading hosting to third-parties (e.g., S3, Cincopa) for performance and scalability.
- Utilize signed URLs/CDNs to secure your media and avoid unauthorized use.
- Store multiple resolutions or versions and have the frontend determined.
- Add captions to your videos, alt descriptions to your images, and audio control.
- Clean URLs, confirm formats, and implement CSP for third-party scripts.
Example: Configuring Custom Middleware in Strapi for Media and Script Security Policies
// config/middleware.js
module.exports = [ ‘strapi::security’, { name: ‘media-url-sanitizer’, config: { contentSecurityPolicy: { useDefaults: true, directives: { ‘media-src’: [“‘self'”, “https://cdn.cloudinary.com”], ‘script-src’: [“‘self'”, “https://player.vimeo.com”] } } } } ]; |
Explanation:
- name is the name of the middleware, in this case ‘media-url-sanitizer’.
- config holds the configuration details.
- ‘media-src’ controls where media files (images, videos, audio) can be loaded from.
Here, media is allowed from the same origin (‘self’).
- ‘script-src’ controls where scripts can be loaded from. Here, scripts are allowed from the same origin (‘self’) and from https://player.vimeo.com.
Extending Strapi with relevant plugins turns it from a mere media storage solution to a media management backend. By addressing format compatibility, responsive delivery, performance optimization, and security in their entirety, developers can get media-rich sites and apps to load quickly, look amazing, and perform consistently across all devices.