{"id":5157,"date":"2026-02-18T19:06:14","date_gmt":"2026-02-18T11:06:14","guid":{"rendered":"https:\/\/crepal.ai\/blog\/?p=5157"},"modified":"2026-02-18T19:06:16","modified_gmt":"2026-02-18T11:06:16","slug":"blog-how-to-use-custom-fonts-in-remotion","status":"publish","type":"post","link":"https:\/\/crepal.ai\/blog\/aivideo\/blog-how-to-use-custom-fonts-in-remotion\/","title":{"rendered":"How to Use Custom Fonts in Remotion (Local Fonts + Google Fonts, Render-Safe)"},"content":{"rendered":"\n<p>Hey, I&#8217;m Dora. I was working on a brand video last Tuesday \u2014 client sent over their custom font files, I loaded them the &#8220;normal&#8221; way I&#8217;d use in any React app, previewed in Studio, everything looked perfect. Hit render, downloaded the MP4, and&#8230; all the text was in Arial.<\/p>\n\n\n\n<p>Three hours later, I finally figured out why. <a href=\"https:\/\/www.remotion.dev\/docs\/render\" target=\"_blank\" rel=\"noreferrer noopener nofollow\">Remotion&#8217;s rendering process<\/a> doesn&#8217;t work like regular browser rendering. Fonts that load fine in preview can completely disappear during the actual render if you don&#8217;t load them correctly.<\/p>\n\n\n\n<p>I documented the workflows that actually survive rendering \u2014 both for local custom fonts and Google Fonts.<\/p>\n\n\n\n<figure class=\"wp-block-gallery has-nested-images columns-default is-cropped wp-block-gallery-1 is-layout-flex wp-block-gallery-is-layout-flex\">\n<figure class=\"wp-block-image size-large\"><img decoding=\"async\" width=\"1024\" height=\"459\" data-id=\"5159\" data-src=\"https:\/\/crepal.ai\/blog\/wp-content\/uploads\/2026\/02\/image-56-1024x459.png\" alt=\"\" class=\"wp-image-5159 lazyload\" data-srcset=\"https:\/\/crepal.ai\/blog\/wp-content\/uploads\/2026\/02\/image-56-1024x459.png 1024w, https:\/\/crepal.ai\/blog\/wp-content\/uploads\/2026\/02\/image-56-300x134.png 300w, https:\/\/crepal.ai\/blog\/wp-content\/uploads\/2026\/02\/image-56-768x344.png 768w, https:\/\/crepal.ai\/blog\/wp-content\/uploads\/2026\/02\/image-56-18x8.png 18w, https:\/\/crepal.ai\/blog\/wp-content\/uploads\/2026\/02\/image-56.png 1045w\" data-sizes=\"auto, (max-width: 1024px) 100vw, 1024px\" src=\"data:image\/svg+xml;base64,PHN2ZyB3aWR0aD0iMSIgaGVpZ2h0PSIxIiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciPjwvc3ZnPg==\" style=\"--smush-placeholder-width: 1024px; --smush-placeholder-aspect-ratio: 1024\/459;\" \/><\/figure>\n<\/figure>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"why-fonts-break-ssr-bundling-caching\">Why Fonts Break (SSR, Bundling, Caching)<\/h2>\n\n\n\n<p>Before diving into solutions, here&#8217;s why fonts break in Remotion when they work fine in regular React apps.<\/p>\n\n\n\n<p><strong>The rendering process:<\/strong><\/p>\n\n\n\n<p>Remotion Studio uses your browser&#8217;s normal rendering \u2014 fonts load from cache, CDN, local files like any webpage.<\/p>\n\n\n\n<p>But when rendering video, Remotion uses headless Chrome (Puppeteer-based). This headless browser:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>Starts with zero cache<\/li>\n\n\n\n<li>Loads each frame independently<\/li>\n\n\n\n<li>Has strict timing requirements<\/li>\n\n\n\n<li>Can&#8217;t rely on external network requests completing in time<\/li>\n<\/ul>\n\n\n\n<p><strong>Three failure modes:<\/strong><\/p>\n\n\n\n<ol start=\"1\" class=\"wp-block-list\">\n<li><strong>Race conditions:<\/strong> Google Font CSS <code>@import<\/code> might not finish downloading before frames render. You get Arial in the final video.<\/li>\n\n\n\n<li><strong>Missing bundled files:<\/strong> Relative paths like <code>..\/fonts\/custom.woff2<\/code> might not get included in the build. Font file just isn&#8217;t there.<\/li>\n\n\n\n<li><strong>SSR<\/strong><strong>\/hydration issues:<\/strong> Fonts loaded with client-side-only APIs (like <code>useEffect<\/code> without <code>delayRender<\/code>) won&#8217;t be available during frame generation.<\/li>\n<\/ol>\n\n\n\n<p>I hit all three on different projects in February. First time, I thought it was a Remotion bug. Nope \u2014 just misunderstanding headless rendering.<\/p>\n\n\n\n<p>While, the moment the font changed to Arial, actually half of the problem had been solved. The real effort was spent on clarifying why it failed, which approaches were false, and which ones were reliable.<\/p>\n\n\n\n<p>What we did in Crepal was to systematically document these &#8220;issues that only surface during the rendering stage&#8221;, so that they won&#8217;t steal three hours of your time again next time.<\/p>\n\n\n\n<p><strong><a href=\"https:\/\/crepal.ai\/?utm_source=chatgpt.com\" target=\"_blank\" rel=\"noreferrer noopener nofollow\">Try Crepal for free!<\/a><\/strong><\/p>\n\n\n\n<figure class=\"wp-block-gallery has-nested-images columns-default is-cropped wp-block-gallery-2 is-layout-flex wp-block-gallery-is-layout-flex\">\n<figure class=\"wp-block-image size-large\"><img decoding=\"async\" width=\"1024\" height=\"666\" data-id=\"5160\" data-src=\"https:\/\/crepal.ai\/blog\/wp-content\/uploads\/2026\/02\/image-57-1024x666.png\" alt=\"\" class=\"wp-image-5160 lazyload\" data-srcset=\"https:\/\/crepal.ai\/blog\/wp-content\/uploads\/2026\/02\/image-57-1024x666.png 1024w, https:\/\/crepal.ai\/blog\/wp-content\/uploads\/2026\/02\/image-57-300x195.png 300w, https:\/\/crepal.ai\/blog\/wp-content\/uploads\/2026\/02\/image-57-768x500.png 768w, https:\/\/crepal.ai\/blog\/wp-content\/uploads\/2026\/02\/image-57-18x12.png 18w, https:\/\/crepal.ai\/blog\/wp-content\/uploads\/2026\/02\/image-57.png 1326w\" data-sizes=\"auto, (max-width: 1024px) 100vw, 1024px\" src=\"data:image\/svg+xml;base64,PHN2ZyB3aWR0aD0iMSIgaGVpZ2h0PSIxIiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciPjwvc3ZnPg==\" style=\"--smush-placeholder-width: 1024px; --smush-placeholder-aspect-ratio: 1024\/666;\" \/><\/figure>\n<\/figure>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"local-font-loading-that-survives-rendering\">Local Font Loading That Survives Rendering<\/h2>\n\n\n\n<p>For custom fonts, use the <a href=\"https:\/\/www.remotion.dev\/docs\/fonts-api\/\" target=\"_blank\" rel=\"noreferrer noopener nofollow\">@remotion\/fonts package<\/a>.<\/p>\n\n\n\n<p><strong>Step 1: Install<\/strong><\/p>\n\n\n\n<p>bash<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>npm install @remotion\/fonts<\/code><\/pre>\n\n\n\n<p><strong>Step 2: Put fonts in <\/strong><strong><code>public\/<\/code><\/strong><\/p>\n\n\n\n<p>Critical \u2014 fonts must be in <code>public\/<\/code>:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>public\/\n\u2514\u2500\u2500 fonts\/\n    \u251c\u2500\u2500 CustomFont-Regular.woff2\n    \u251c\u2500\u2500 CustomFont-Bold.woff2\n    \u2514\u2500\u2500 CustomFont-Italic.woff2<\/code><\/pre>\n\n\n\n<p><strong>Step 3: Load fonts<\/strong><\/p>\n\n\n\n<p>According to the <a href=\"https:\/\/www.remotion.dev\/docs\/fonts-api\/load-font\" target=\"_blank\" rel=\"noreferrer noopener nofollow\">font loading guide<\/a>:<\/p>\n\n\n\n<p>javascript<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>import { loadFont } from '@remotion\/fonts';\nimport { staticFile } from 'remotion';\n\nloadFont({\n  family: 'CustomFont',\n  url: staticFile('fonts\/CustomFont-Regular.woff2'),\n  weight: '400',\n});\n\nloadFont({\n  family: 'CustomFont',\n  url: staticFile('fonts\/CustomFont-Bold.woff2'),\n  weight: '700',\n});<\/code><\/pre>\n\n\n\n<p><strong>Use <\/strong><strong><code>staticFile()<\/code><\/strong><strong> \u2014 not relative imports.<\/strong><\/p>\n\n\n\n<p><strong>Step 4: Use the font<\/strong><\/p>\n\n\n\n<p>javascript<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>&lt;div style={{ fontFamily: 'CustomFont', fontWeight: 700 }}&gt;\n  Text in your custom font\n&lt;\/div&gt;<\/code><\/pre>\n\n\n\n<p><strong>Why this works:<\/strong><\/p>\n\n\n\n<p><code>loadFont()<\/code> uses <code>delayRender()<\/code> internally. Remotion won&#8217;t render frames until fonts are loaded.<\/p>\n\n\n\n<p>I tested this Feb 6th with a client&#8217;s branded typeface. Three weights, 2-minute video \u2014 every frame had the correct font.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"font-fallback-strategy-multi-language-subtitles\">Font Fallback Strategy (Multi-Language Subtitles)<\/h3>\n\n\n\n<p>If you&#8217;re rendering videos with multi-language subtitles (e.g., English + Japanese + Arabic), you need a fallback strategy. No single font supports every Unicode range.<\/p>\n\n\n\n<p><strong>The problem:<\/strong><\/p>\n\n\n\n<p>Your custom English font might not have Japanese characters. If you specify <code>fontFamily: 'CustomFont'<\/code> and try to render Japanese text, you&#8217;ll get those ugly \ufffd replacement characters or blank boxes.<\/p>\n\n\n\n<p><strong>Solution: Font stacking with Unicode ranges<\/strong><\/p>\n\n\n\n<p>javascript<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code><em>\/\/ Load English font<\/em>\nloadFont({\n  family: 'CustomFont',\n  url: staticFile('fonts\/CustomFont-Regular.woff2'),\n  weight: '400',\n  unicodeRange: 'U+0000-00FF, U+0131, U+0152-0153', <em>\/\/ Latin characters<\/em>\n});\n\n<em>\/\/ Load Japanese font for CJK characters<\/em>\nloadFont({\n  family: 'NotoSansCJK',\n  url: staticFile('fonts\/NotoSansCJKjp-Regular.otf'),\n  weight: '400',\n  unicodeRange: 'U+3000-9FFF, U+FF00-FFEF', <em>\/\/ Japanese\/CJK range<\/em>\n});\n\n<em>\/\/ Load Arabic font<\/em>\nloadFont({\n  family: 'NotoSansArabic',\n  url: staticFile('fonts\/NotoSansArabic-Regular.ttf'),\n  weight: '400',\n  unicodeRange: 'U+0600-06FF, U+0750-077F', <em>\/\/ Arabic range<\/em>\n});<\/code><\/pre>\n\n\n\n<p>Then use font stacking in CSS:<\/p>\n\n\n\n<p>javascript<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>&lt;div style={{\n  fontFamily: '\"CustomFont\", \"NotoSansCJK\", \"NotoSansArabic\", sans-serif',\n}}&gt;\n  {subtitleText}\n&lt;\/div&gt;<\/code><\/pre>\n\n\n\n<p>The browser will use:<\/p>\n\n\n\n<ol start=\"1\" class=\"wp-block-list\">\n<li>CustomFont for Latin characters<\/li>\n\n\n\n<li>NotoSansCJK for Japanese\/Chinese\/Korean<\/li>\n\n\n\n<li>NotoSansArabic for Arabic text<\/li>\n\n\n\n<li>System sans-serif as final fallback<\/li>\n<\/ol>\n\n\n\n<p><strong>Unicode range reference:<\/strong><\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li><strong>Latin (English, Spanish, French):<\/strong><code>U+0000-00FF, U+0100-024F<\/code><\/li>\n\n\n\n<li><strong>Cyrillic (Russian):<\/strong><code>U+0400-04FF<\/code><\/li>\n\n\n\n<li><strong>CJK (Chinese, Japanese, Korean):<\/strong><code>U+3000-9FFF, U+FF00-FFEF<\/code><\/li>\n\n\n\n<li><strong>Arabic:<\/strong><code>U+0600-06FF<\/code><\/li>\n\n\n\n<li><strong>Hebrew:<\/strong><code>U+0590-05FF<\/code><\/li>\n\n\n\n<li><strong>Thai:<\/strong><code>U+0E00-0E7F<\/code><\/li>\n<\/ul>\n\n\n\n<p>I tested this with English + Japanese subtitles on February 7th. English used a custom brand font, Japanese used Noto Sans CJK. Rendered perfectly \u2014 no character replacement issues.<\/p>\n\n\n\n<p><strong>Tip:<\/strong> Use <a href=\"https:\/\/fonts.google.com\/noto\" target=\"_blank\" rel=\"noreferrer noopener nofollow\">Noto fonts<\/a> for fallback coverage. They&#8217;re designed specifically for comprehensive language support.<\/p>\n\n\n\n<figure class=\"wp-block-gallery has-nested-images columns-default is-cropped wp-block-gallery-3 is-layout-flex wp-block-gallery-is-layout-flex\">\n<figure class=\"wp-block-image size-large\"><img decoding=\"async\" width=\"1024\" height=\"715\" data-id=\"5161\" data-src=\"https:\/\/crepal.ai\/blog\/wp-content\/uploads\/2026\/02\/image-58-1024x715.png\" alt=\"\" class=\"wp-image-5161 lazyload\" data-srcset=\"https:\/\/crepal.ai\/blog\/wp-content\/uploads\/2026\/02\/image-58-1024x715.png 1024w, https:\/\/crepal.ai\/blog\/wp-content\/uploads\/2026\/02\/image-58-300x209.png 300w, https:\/\/crepal.ai\/blog\/wp-content\/uploads\/2026\/02\/image-58-768x536.png 768w, https:\/\/crepal.ai\/blog\/wp-content\/uploads\/2026\/02\/image-58-18x12.png 18w, https:\/\/crepal.ai\/blog\/wp-content\/uploads\/2026\/02\/image-58.png 1129w\" data-sizes=\"auto, (max-width: 1024px) 100vw, 1024px\" src=\"data:image\/svg+xml;base64,PHN2ZyB3aWR0aD0iMSIgaGVpZ2h0PSIxIiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciPjwvc3ZnPg==\" style=\"--smush-placeholder-width: 1024px; --smush-placeholder-aspect-ratio: 1024\/715;\" \/><\/figure>\n<\/figure>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"google-fonts-with-remotion-packages-safe-workflow\">Google Fonts with Remotion Packages (Safe Workflow)<\/h2>\n\n\n\n<p>For Google Fonts, use the <a href=\"https:\/\/www.remotion.dev\/docs\/google-fonts\" target=\"_blank\" rel=\"noreferrer noopener nofollow\">@remotion\/google-fonts package<\/a>.<\/p>\n\n\n\n<p><strong>Why not CSS imports?<\/strong><\/p>\n\n\n\n<p>You <em>can<\/em> import CSS:<\/p>\n\n\n\n<p>css<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>@import url('https:\/\/fonts.googleapis.com\/css2?family=Roboto');<\/code><\/pre>\n\n\n\n<p>According to the <a href=\"https:\/\/www.remotion.dev\/docs\/fonts\" target=\"_blank\" rel=\"noreferrer noopener nofollow\">fonts docs<\/a>, from v2.2+, Remotion waits for fonts automatically.<\/p>\n\n\n\n<p>But issues exist:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>Network dependency (slow CDN = slow renders)<\/li>\n\n\n\n<li>No type safety<\/li>\n\n\n\n<li>Harder to manage variants<\/li>\n<\/ul>\n\n\n\n<p><strong>Better: Use <\/strong><strong><code>@remotion\/google-fonts<\/code><\/strong><\/p>\n\n\n\n<p><strong>Install:<\/strong><\/p>\n\n\n\n<p>bash<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>npm install @remotion\/google-fonts<\/code><\/pre>\n\n\n\n<p><strong>Load and use:<\/strong><\/p>\n\n\n\n<p>javascript<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>import { loadFont } from '@remotion\/google-fonts\/Montserrat';\n\nconst { fontFamily } = loadFont();\n\n&lt;div style={{ fontFamily }}&gt;Text in Montserrat&lt;\/div&gt;<\/code><\/pre>\n\n\n\n<p><strong>Load specific weights:<\/strong><\/p>\n\n\n\n<p>javascript<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>loadFont('normal', { weights: &#091;'400', '700'] });\nloadFont('italic', { weights: &#091;'400', '700'] });<\/code><\/pre>\n\n\n\n<p>Type-safe \u2014 autocomplete for weights and styles.<\/p>\n\n\n\n<p><strong>Why better:<\/strong><\/p>\n\n\n\n<ol start=\"1\" class=\"wp-block-list\">\n<li>Type safety with autocomplete<\/li>\n\n\n\n<li>Optimized loading (only variants you use)<\/li>\n\n\n\n<li>Subset support (latin, cyrillic, etc.)<\/li>\n\n\n\n<li>Fonts bundled (no CDN calls during render)<\/li>\n<\/ol>\n\n\n\n<p>I switched from CSS to <code>@remotion\/google-fonts<\/code> on Feb 6th. Render time dropped ~15% \u2014 fonts bundled instead of fetched.<\/p>\n\n\n\n<p><strong>Multiple fonts:<\/strong><\/p>\n\n\n\n<p>javascript<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>import { loadFont as loadMontserrat } from '@remotion\/google-fonts\/Montserrat';\nimport { loadFont as loadRoboto } from '@remotion\/google-fonts\/Roboto';\n\nconst m = loadMontserrat();\nconst r = loadRoboto();\n\n&lt;h1 style={{ fontFamily: m.fontFamily }}&gt;Heading&lt;\/h1&gt;\n&lt;p style={{ fontFamily: r.fontFamily }}&gt;Body&lt;\/p&gt;<\/code><\/pre>\n\n\n\n<p><strong>Subset optimization:<\/strong><\/p>\n\n\n\n<p>javascript<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>loadFont('normal', {\n  weights: &#091;'400', '700'],\n  subsets: &#091;'latin'], <em>\/\/ Skip cyrillic, greek, etc.<\/em>\n});<\/code><\/pre>\n\n\n\n<p>Reduces file size, faster loading.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"faq\">FAQ<\/h2>\n\n\n\n<p><strong>Why does my font work in Studio but not in the final render?<\/strong><\/p>\n\n\n\n<p>Remotion Studio uses your browser&#8217;s normal rendering, which has cached fonts and access to your local system fonts. Renders use a fresh headless Chrome instance with no cache. Use <code>@remotion\/fonts<\/code> or <code>@remotion\/google-fonts<\/code> with <code>loadFont()<\/code> to ensure fonts are explicitly loaded before rendering starts.<\/p>\n\n\n\n<p><strong>Can I use system fonts (like Arial, Helvetica)?<\/strong><\/p>\n\n\n\n<p>Yes, but be careful. System fonts available on macOS might not be available in the headless Chrome instance (especially on Linux render servers). Stick to web-safe fonts or bundle your own for consistency.<\/p>\n\n\n\n<p><strong>What font formats does Remotion support?<\/strong><\/p>\n\n\n\n<p>WOFF2, WOFF, TTF, OTF. According to the <a href=\"https:\/\/www.remotion.dev\/docs\/fonts-api\/load-font\" target=\"_blank\" rel=\"noreferrer noopener nofollow\">loadFont() API docs<\/a>, WOFF2 is recommended \u2014 smallest file size, best compression.<\/p>\n\n\n\n<figure class=\"wp-block-gallery has-nested-images columns-default is-cropped wp-block-gallery-4 is-layout-flex wp-block-gallery-is-layout-flex\">\n<figure class=\"wp-block-image size-large\"><img decoding=\"async\" width=\"1024\" height=\"423\" data-id=\"5162\" data-src=\"https:\/\/crepal.ai\/blog\/wp-content\/uploads\/2026\/02\/image-59-1024x423.png\" alt=\"\" class=\"wp-image-5162 lazyload\" data-srcset=\"https:\/\/crepal.ai\/blog\/wp-content\/uploads\/2026\/02\/image-59-1024x423.png 1024w, https:\/\/crepal.ai\/blog\/wp-content\/uploads\/2026\/02\/image-59-300x124.png 300w, https:\/\/crepal.ai\/blog\/wp-content\/uploads\/2026\/02\/image-59-768x318.png 768w, https:\/\/crepal.ai\/blog\/wp-content\/uploads\/2026\/02\/image-59-18x7.png 18w, https:\/\/crepal.ai\/blog\/wp-content\/uploads\/2026\/02\/image-59.png 1156w\" data-sizes=\"auto, (max-width: 1024px) 100vw, 1024px\" src=\"data:image\/svg+xml;base64,PHN2ZyB3aWR0aD0iMSIgaGVpZ2h0PSIxIiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciPjwvc3ZnPg==\" style=\"--smush-placeholder-width: 1024px; --smush-placeholder-aspect-ratio: 1024\/423;\" \/><\/figure>\n<\/figure>\n\n\n\n<p><strong>How do I handle font licensing for commercial projects?<\/strong><\/p>\n\n\n\n<p>Check the font&#8217;s license. Google Fonts are all open-source (SIL, Apache, etc.) \u2014 free for commercial use. Custom fonts might have licensing restrictions on video usage. When in doubt, contact the font creator.<\/p>\n\n\n\n<p><strong>Can I use variable fonts (fonts with adjustable weight\/width)?<\/strong><\/p>\n\n\n\n<p>Yes. Load the variable font file, then use CSS properties like <code>font-variation-settings<\/code>:<\/p>\n\n\n\n<p>javascript<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>loadFont({\n  family: 'InterVariable',\n  url: staticFile('fonts\/Inter-Variable.woff2'),\n});\n\n<em>\/\/ Use with variation<\/em>\n&lt;div style={{\n  fontFamily: 'InterVariable',\n  fontVariationSettings: '\"wght\" 650', <em>\/\/ Custom weight<\/em>\n}}&gt;\n  Text\n&lt;\/div&gt;<\/code><\/pre>\n\n\n\n<p>Variable fonts reduce the number of font files you need (one file for all weights).<\/p>\n\n\n\n<p><strong>Why is my render slow with lots of fonts?<\/strong><\/p>\n\n\n\n<p>Each <code>loadFont()<\/code> call adds to initial load time. Only load the fonts and weights you actually use. For multi-language projects, use Unicode ranges to avoid loading entire font families unnecessarily.<\/p>\n\n\n\n<p><strong>What happens if a font fails to load?<\/strong><\/p>\n\n\n\n<p>The browser falls back to the next font in your <code>fontFamily<\/code> stack. Always specify fallbacks:<\/p>\n\n\n\n<p>javascript<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>fontFamily: '\"CustomFont\", \"Helvetica Neue\", Arial, sans-serif'<\/code><\/pre>\n\n\n\n<p>If all fail, the browser uses the system default.<\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\" \/>\n\n\n\n<p>The biggest lesson from testing fonts in February: never trust preview-only results. Always do a 10-second test render before committing to a full render.<\/p>\n\n\n\n<p>For local fonts, <code>@remotion\/fonts<\/code> with <code>staticFile()<\/code> is bulletproof. For Google Fonts, <code>@remotion\/google-fonts<\/code> gives you type safety and faster renders. Both methods use <code>delayRender()<\/code> internally, so fonts are guaranteed to load before frame generation starts.<\/p>\n\n\n\n<p>One more tip: if you&#8217;re switching between local development and cloud rendering (like Remotion Lambda), keep all fonts in <code>public\/<\/code> and use <code>staticFile()<\/code> consistently. This avoids path issues across different environments.<\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\" \/>\n\n\n\n<p><strong>Previous posts:<\/strong><\/p>\n\n\n\n<figure class=\"wp-block-embed is-type-wp-embed is-provider-crepal-content-center wp-block-embed-crepal-content-center\"><div class=\"wp-block-embed__wrapper\">\n<blockquote class=\"wp-embedded-content\" data-secret=\"MQpjvtedLn\"><a href=\"https:\/\/crepal.ai\/blog\/aivideo\/blog-how-to-create-tiktok-style-captions-remotion\/\">How to Create TikTok-Style Captions in Remotion (SRT Import + Word Highlight)<\/a><\/blockquote><iframe class=\"wp-embedded-content lazyload\" sandbox=\"allow-scripts\" security=\"restricted\" style=\"position: absolute; visibility: hidden;\" title=\"\u300a How to Create TikTok-Style Captions in Remotion (SRT Import + Word Highlight) \u300b\u2014CrePal Content Center\" data-src=\"https:\/\/crepal.ai\/blog\/aivideo\/blog-how-to-create-tiktok-style-captions-remotion\/embed\/#?secret=DuAp2mTYaz#?secret=MQpjvtedLn\" data-secret=\"MQpjvtedLn\" width=\"600\" height=\"338\" frameborder=\"0\" marginwidth=\"0\" marginheight=\"0\" scrolling=\"no\" src=\"data:image\/svg+xml;base64,PHN2ZyB3aWR0aD0iMSIgaGVpZ2h0PSIxIiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciPjwvc3ZnPg==\" data-load-mode=\"1\"><\/iframe>\n<\/div><\/figure>\n\n\n\n<figure class=\"wp-block-embed is-type-wp-embed is-provider-crepal-content-center wp-block-embed-crepal-content-center\"><div class=\"wp-block-embed__wrapper\">\n<blockquote class=\"wp-embedded-content\" data-secret=\"WDexlvJbl1\"><a href=\"https:\/\/crepal.ai\/blog\/aivideo\/blog-how-to-fix-remotion-render-failed\/\">How to Fix \u201cRemotion Render Failed\u201d (FFmpeg\/FFprobe, Missing Assets, Decode Errors)<\/a><\/blockquote><iframe class=\"wp-embedded-content lazyload\" sandbox=\"allow-scripts\" security=\"restricted\" style=\"position: absolute; visibility: hidden;\" title=\"\u300a How to Fix \u201cRemotion Render Failed\u201d (FFmpeg\/FFprobe, Missing Assets, Decode Errors) \u300b\u2014CrePal Content Center\" data-src=\"https:\/\/crepal.ai\/blog\/aivideo\/blog-how-to-fix-remotion-render-failed\/embed\/#?secret=CFajSrqTDU#?secret=WDexlvJbl1\" data-secret=\"WDexlvJbl1\" width=\"600\" height=\"338\" frameborder=\"0\" marginwidth=\"0\" marginheight=\"0\" scrolling=\"no\" src=\"data:image\/svg+xml;base64,PHN2ZyB3aWR0aD0iMSIgaGVpZ2h0PSIxIiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciPjwvc3ZnPg==\" data-load-mode=\"1\"><\/iframe>\n<\/div><\/figure>\n\n\n\n<figure class=\"wp-block-embed is-type-wp-embed is-provider-crepal-content-center wp-block-embed-crepal-content-center\"><div class=\"wp-block-embed__wrapper\">\n<blockquote class=\"wp-embedded-content\" data-secret=\"QsZJLwr5sG\"><a href=\"https:\/\/crepal.ai\/blog\/aivideo\/blog-how-to-install-remotion-skills\/\">How to Install Remotion Agent Skills and Verify They Work (in 5 Minutes)<\/a><\/blockquote><iframe class=\"wp-embedded-content lazyload\" sandbox=\"allow-scripts\" security=\"restricted\" style=\"position: absolute; visibility: hidden;\" title=\"\u300a How to Install Remotion Agent Skills and Verify They Work (in 5 Minutes) \u300b\u2014CrePal Content Center\" data-src=\"https:\/\/crepal.ai\/blog\/aivideo\/blog-how-to-install-remotion-skills\/embed\/#?secret=npaPNNc8hk#?secret=QsZJLwr5sG\" data-secret=\"QsZJLwr5sG\" width=\"600\" height=\"338\" frameborder=\"0\" marginwidth=\"0\" marginheight=\"0\" scrolling=\"no\" src=\"data:image\/svg+xml;base64,PHN2ZyB3aWR0aD0iMSIgaGVpZ2h0PSIxIiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciPjwvc3ZnPg==\" data-load-mode=\"1\"><\/iframe>\n<\/div><\/figure>\n","protected":false},"excerpt":{"rendered":"<p>Hey, I&#8217;m Dora. I was working on a brand video last Tuesday \u2014 client sent over their custom font files, I loaded them the &#8220;normal&#8221; way I&#8217;d use in any React app, previewed in Studio, everything looked perfect. Hit render, downloaded the MP4, and&#8230; all the text was in Arial. Three hours later, I finally [&hellip;]<\/p>\n","protected":false},"author":5,"featured_media":5158,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"_gspb_post_css":"","_uag_custom_page_level_css":"","footnotes":""},"categories":[8],"tags":[],"class_list":["post-5157","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-aivideo"],"blocksy_meta":[],"uagb_featured_image_src":{"full":["https:\/\/crepal.ai\/blog\/wp-content\/uploads\/2026\/02\/image-55.png",1280,720,false],"thumbnail":["https:\/\/crepal.ai\/blog\/wp-content\/uploads\/2026\/02\/image-55-150x150.png",150,150,true],"medium":["https:\/\/crepal.ai\/blog\/wp-content\/uploads\/2026\/02\/image-55-300x169.png",300,169,true],"medium_large":["https:\/\/crepal.ai\/blog\/wp-content\/uploads\/2026\/02\/image-55-768x432.png",768,432,true],"large":["https:\/\/crepal.ai\/blog\/wp-content\/uploads\/2026\/02\/image-55-1024x576.png",1024,576,true],"1536x1536":["https:\/\/crepal.ai\/blog\/wp-content\/uploads\/2026\/02\/image-55.png",1280,720,false],"2048x2048":["https:\/\/crepal.ai\/blog\/wp-content\/uploads\/2026\/02\/image-55.png",1280,720,false],"trp-custom-language-flag":["https:\/\/crepal.ai\/blog\/wp-content\/uploads\/2026\/02\/image-55-18x10.png",18,10,true]},"uagb_author_info":{"display_name":"Dora","author_link":"https:\/\/crepal.ai\/blog\/author\/dora\/"},"uagb_comment_info":1,"uagb_excerpt":"Hey, I&#8217;m Dora. I was working on a brand video last Tuesday \u2014 client sent over their custom font files, I loaded them the &#8220;normal&#8221; way I&#8217;d use in any React app, previewed in Studio, everything looked perfect. Hit render, downloaded the MP4, and&#8230; all the text was in Arial. Three hours later, I finally&hellip;","_links":{"self":[{"href":"https:\/\/crepal.ai\/blog\/wp-json\/wp\/v2\/posts\/5157","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/crepal.ai\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/crepal.ai\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/crepal.ai\/blog\/wp-json\/wp\/v2\/users\/5"}],"replies":[{"embeddable":true,"href":"https:\/\/crepal.ai\/blog\/wp-json\/wp\/v2\/comments?post=5157"}],"version-history":[{"count":1,"href":"https:\/\/crepal.ai\/blog\/wp-json\/wp\/v2\/posts\/5157\/revisions"}],"predecessor-version":[{"id":5163,"href":"https:\/\/crepal.ai\/blog\/wp-json\/wp\/v2\/posts\/5157\/revisions\/5163"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/crepal.ai\/blog\/wp-json\/wp\/v2\/media\/5158"}],"wp:attachment":[{"href":"https:\/\/crepal.ai\/blog\/wp-json\/wp\/v2\/media?parent=5157"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/crepal.ai\/blog\/wp-json\/wp\/v2\/categories?post=5157"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/crepal.ai\/blog\/wp-json\/wp\/v2\/tags?post=5157"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}