
Managing Content on Small Websites: Guide for 2025 & Beyond
22 Nov 2022
Almost every website needs video. Maybe it’s a product demo. Maybe it’s an interview or a loop in the background. Video helps explain things better. So you need a way to manage and show it properly.
Here’s how to do that in BCMS.
In BCMS, video is just content block. You create a widget for it like any other block.
A basic video widget should have:
A media field (file
) for uploaded videos
A boolean (autoplay
) to control playback
A string field (youtube_url
) if you want to support YouTube embeds too
The generated TypeScript type will look like this:
export interface VideoWidget { file?: PropMediaDataParsed; autoplay?: boolean; youtube_url?: string; }
Once you have the widget, make a React (Next.js) component to show the video.
This one checks if a video was uploaded. If not, it checks if there’s a YouTube URL.
'use client'; import React, { FC, useEffect, useState } from 'react'; import type { VideoWidget as VideoWidgetType } from '@bcms-types/ts'; import BCMSMedia from '../bcms-media'; import { bcmsMediaClient } from '@root/media-client'; interface Props { data: VideoWidgetType; } const VideoWidget: FC<Props> = ({ data }) => { const [youtubeVideoId, setYoutubeVideoId] = useState(''); useEffect(() => { if (!data.youtube_url) return; const regExp = /^.*((youtu.be\/)|(v\/)|(\/u\/\w\/)|(embed\/)|(watch\?))\??v?=?([^#&?]*).*/; const match = data.youtube_url.match(regExp); setYoutubeVideoId(match && match[7].length === 11 ? match[7] : ''); }, [data.youtube_url]); return ( <div> {data.file ? ( <BCMSMedia video={{ src: bcmsMediaClient.cmsOrigin + bcmsMediaClient.media.toUri(data.file._id, data.file.name), autoplay: data.autoplay, }} /> ) : youtubeVideoId ? ( <iframe src={`https://www.youtube.com/embed/${youtubeVideoId}`} allow="autoplay; encrypted-media" allowFullScreen className="aspect-video w-full" title="YouTube video" /> ) : null} </div> ); }; export default VideoWidget;
bcmsMediaClient
and why it's separateIn most of my BCMS setups, you’ll see two client instances:
client.ts
- used for authenticated actions like fetching content
media-client.ts
- used only for accessing public media
I keep bcmsMediaClient
separate because it's safe to expose in the browser. It only includes public keys and is used for loading images and videos from the BCMS CDN. Nothing sensitive is in there. But still, no need to use it with the content API key.
Here’s what it looks like:
// media-client.ts import { Client } from '@thebcms/client'; export const bcmsMediaClient = new Client( process.env.NEXT_PUBLIC_BCMS_ORG_ID!, process.env.NEXT_PUBLIC_BCMS_INSTANCE_ID!, { id: process.env.NEXT_PUBLIC_BCMS_MEDIA_API_KEY_ID!, secret: process.env.NEXT_PUBLIC_BCMS_MEDIA_API_KEY_SECRET!, }, { injectSvg: true, }, );
You use it to:
Generate media URLs: bcmsMediaClient.media.toUri(...)
Pass config to < BCMSImage />
or < BCMSContentManager />
Avoid exposing full API access in the browser
If you only use one client for everything, you might leak sensitive keys in the frontend. That’s why I always create bcmsMediaClient
separately.
To keep things clean, you can use a small helper component like BCMSMedia
to render both images and videos. Here’s the version for video only:
'use client'; import React, { FC } from 'react'; interface Props { video?: { src: string; autoplay?: boolean; }; className?: string; } const BCMSMedia: FC<Props> = ({ video, className = '' }) => { if (!video) return null; return ( <video controls={!video.autoplay} autoPlay={video.autoplay} playsInline={video.autoplay} muted={video.autoplay} loop={video.autoplay} className={`w-full object-contain ${className}`} > <source src={video.src} type="video/mp4" /> Your browser does not support the video element. </video> ); }; export default BCMSMedia;
When you upload a video in the dashboard, BCMS stores it on our CDN. You get a public URL. That’s what you use in the < video >
tag.
You don’t need to worry about hosting or storage.
But BCMS doesn’t compress or resize videos. It gives you the original file. So make sure your videos are optimized before uploading.
Right now, uploading is only possible through the dashboard. API upload is coming soon, but only in the Pro version.
If you need more control - like resizing, transcoding, adaptive streaming - you can use Cloudinary.
Store the Cloudinary URL in your widget. Then render it however you want. BCMS doesn’t care where the video is hosted. You control that part.
If you're using BCMSContentManager
to render dynamic content, just pass the VideoWidget
like this:
import { BCMSContentManager } from '@thebcms/components-react'; import VideoWidget from '@/components/widgets/video'; export default function BlogContent({ content }) { return ( <BCMSContentManager items={content} clientConfig={bcmsMediaClient.getConfig()} widgetComponents={{ video: VideoWidget, }} /> ); }
You can upload .mp4
files to BCMS and display them on your site
You can also embed YouTube videos
Videos are served from the CDN in their original format
No file upload API yet, but it’s on the roadmap
You’re free to use Cloudinary or any other service
BCMS just gives you the data - you decide how to render it
If you want more control, build your widget and component logic around your needs. That’s the point of BCMS - it stays out of your way.
....
Get all the latest BCMS updates, news and events.
By submitting this form you consent to us emailing you occasionally about our products and services. You can unsubscribe from emails at any time, and we will never pass your email to third parties.
There are many actionable insights in this blog post. Learn more: