Setup Google Fonts
Load and use Google Fonts in your video editor projects
RVE uses Google Fonts for text items and captions. Fonts work in two contexts: the editor (browser) and server-side rendering. The setup uses a proxy route so the large font database (~2MB) stays server-side.
Why a proxy?
Google Fonts need metadata to work - what weights are available, what character sets they support, where to download the actual font files from. For 243 fonts, that metadata adds up to roughly 2MB.
You can't ship 2MB of font data to the browser. It would destroy your page load time. So we split it:
- Browser gets a tiny list - just font names, enough to show a font picker. This is small and fast.
- Server holds the full database - all the metadata. When a user picks a font in the editor, the browser asks your server for just that one font's info via
/api/fonts/[name]. One small request, on demand. - During rendering - when you export a video, the server already has the full database locally. It looks up the fonts that the video uses, gets their metadata, and hands it to the renderer so it can download and render the correct fonts. No extra network calls needed.
The API route is the bridge. It keeps the heavy data on the server and gives the browser exactly what it needs, when it needs it.
How it works
Browser (editor)
├── Uses GOOGLE_FONTS_LIST (lightweight, 243 fonts, names only) for the font picker UI
└── Fetches full font metadata on demand from /api/fonts/[name] and GOOGLE_FONTS_DATABASE (server-only)
Server (rendering)
├── collectFontInfoFromItems() scans the items of all tracks for fonts used
├── Looks up full metadata from GOOGLE_FONTS_DATABASE
└── Passes fontInfos into inputProps so fonts load during renderThe browser never imports the full database. It fetches individual font info via your API route when a user selects a font.
Step 1: Create the font API route
Create app/api/fonts/[name]/route.ts:
import { NextRequest, NextResponse } from 'next/server';
import { GOOGLE_FONTS_DATABASE } from '@reactvideoeditor/react-video-editor/data/google-fonts';
export async function GET(
_request: NextRequest,
ctx: { params: Promise<{ name: string }> }
) {
const fontFamily = (await ctx.params).name;
const fontInfo = GOOGLE_FONTS_DATABASE.find(
(font) => font.fontFamily === fontFamily
);
if (!fontInfo) {
return NextResponse.json(
{ error: `Font "${fontFamily}" not found` },
{ status: 404 }
);
}
return NextResponse.json(fontInfo, {
headers: { 'Cache-Control': 'public, max-age=3600' },
});
}This route serves font metadata (weights, styles, unicode ranges, URLs) from the pre-built GOOGLE_FONTS_DATABASE. The database is server-only and must never be imported in browser code.
Step 2: Set up font collection for rendering
If you use server-side rendering, collect the font info from the text items and the caption items. Then pass it to the renderer.
Create app/utils/text/collect-font-info-from-items.ts:
import {
GOOGLE_FONTS_DATABASE,
type GoogleFontInfo,
} from '@reactvideoeditor/react-video-editor/data/google-fonts';
import {
type CaptionItem,
type Item,
type TextItem,
ItemType,
} from '@reactvideoeditor/react-video-editor/types';
const isTextItem = (item: Item): item is TextItem => item.type === ItemType.TEXT;
const isCaptionItem = (item: Item): item is CaptionItem => item.type === ItemType.CAPTION;
export const collectFontInfoFromItems = (
items: Item[]
): Record<string, GoogleFontInfo> => {
const fontInfos: Record<string, GoogleFontInfo> = {};
for (const item of items) {
let fontFamily: string | undefined;
if (isTextItem(item)) {
fontFamily = item.styles.fontFamily;
} else if (isCaptionItem(item)) {
// The renderer uses Inter if a caption has no fontFamily.
fontFamily = item.styles?.fontFamily || 'Inter';
}
if (!fontFamily || fontInfos[fontFamily]) continue;
const fontInfo = GOOGLE_FONTS_DATABASE.find(
(font) => font.fontFamily === fontFamily
);
if (fontInfo) {
fontInfos[fontFamily] = fontInfo;
}
}
return fontInfos;
};New captions use the Clean template, so their fontFamily is 'Plus Jakarta Sans'. The 'Inter' fallback applies only to a caption with no fontFamily.
Then in your render route, collect and pass the font infos. The items are in inputProps.tracks, so flatten them first:
import { collectFontInfoFromItems } from '@/utils/text/collect-font-info-from-items';
// In your render handler:
const items = (inputProps.tracks ?? []).flatMap((track) => track.items);
const fontInfos = collectFontInfoFromItems(items);
const inputPropsWithFonts = {
...inputProps,
fontInfos,
};
// Pass inputPropsWithFonts to your rendererStep 3: That's it
With the /api/fonts/[name] route in place:
- The font picker in the editor works with no other configuration. It loads font previews from the Google Fonts CDN. When a user selects a font, it gets the full metadata from your route.
- The editor caches loaded fonts in the browser, so it does not send the same request two times.
- If a font does not load, the editor uses Roboto.
- During server-side rendering, the composition reads the
fontInfosthat you pass ininputProps. Thus it loads the fonts without more network calls to your route.
Package imports
| Import path | Purpose |
|---|---|
@reactvideoeditor/react-video-editor/data/google-fonts-list | Lightweight font list (243 fonts, names only). Safe for the browser. |
@reactvideoeditor/react-video-editor/data/google-fonts | Full font database (~2MB) and the GoogleFontInfo type. Server only. Do not import it in browser code. |