April 13, 2025
O. Wolfson
I wanted a simple way to add variety and personality to my website's hero titles:
show a different font every time a visitor loads the page.
Small changes like this make a design feel alive — dynamic without being chaotic.
After experimenting, I created a pure Server Component in Next.js that randomly picks a font at render time, adjusts the layout intelligently, and remains fully hydration-safe.
Here’s how it works.
Big thanks to Cedilla Studio for letting me use their incredible typefaces.
Their work brings character and unpredictability in the best sense.
Cedilla offers custom fonts, Lightroom presets, and design services.
Check out their work at cedilla.studio and follow @type.otf on Instagram.
This project is built with React / Next.js using pure Server Components.
Here's the basic flow:
localFont
utility.<h1>
element is output with no client-side hydration mismatch.Everything happens server-side.
No JavaScript randomness on the client. No flashing. No hydration errors.
bashrandom-font-hero ├── fonts │ ├── CS-5uper.otf │ ├── CS-Defiant2.woff2 │ ├── CS-Endless.woff2 │ ├── CS-Glare.otf │ └── CS-Noire-Black.otf ├── fonts.ts └── random-font-hero.tsx
fonts.ts
Each font is imported, and we define a font list including layout tweaks for each font.
tsimport localFont from "next/font/local";
export const csDefiant = localFont({
src: "./fonts/CS-Defiant2.woff2",
variable: "--font-cs-defiant",
display: "swap",
});
export const csEndless = localFont({
src: "./fonts/CS-Endless.woff2",
variable: "--font-cs-endless",
display: "swap",
});
export const cs5uper = localFont({
src: "./fonts/CS-5uper.otf",
variable: "--font-cs-5uper",
display: "swap",
});
export const csGlare = localFont({
src: "./fonts/CS-Glare.otf",
variable: "--font-cs-glare",
display: "swap",
});
export const fonts = [
{ name: "CS Defiant", font: csDefiant },
{ name: "CS Endless", font: csEndless },
{ name: "CS 5uper", font: cs5uper },
{ name: "CS Glare", font: csGlare },
];
random-font-hero.tsx
The main component picks a font, adjusts font size and spacing, and renders the hero title.
tsximport { fonts } from "./fonts";
interface HeroTitleProps {
children: React.ReactNode;
className?: string;
}
export default function HeroTitle({
children,
className = "",
}: HeroTitleProps) {
const fontsWithInter = [...fonts];
const randomFont =
fontsWithInter[Math.floor(Math.random() * fontsWithInter.length)];
const fontSizeClass =
randomFont.name === "CS Endless"
? "sm:text-[19rem] text-[7.5rem]"
: randomFont.name === "CS Glare"
? "sm:text-[21rem]"
: "sm:text-[22rem]";
const paddingRightClass = randomFont.name === "CS Endless" ? "pr-8" : "pr-0";
return (
<div className="overflow-hidden">
<h1
className={`
${randomFont.font.className}
${fontSizeClass}
text-[9rem]
font-bold
text-center
m-0 p-0
leading-[1]
${}
${}
`}
>
{children}
);
}
Pure Server-Side Randomness:
The font is picked once during server render, so the initial HTML matches exactly what React expects when hydrating the page. No hydration errors.
Dynamic Layout Tuning:
Each font may have a different visual weight and width. We adjust the font size and padding based on the selected font for a clean and consistent look.
Zero Client-Side Flicker:
There’s no flashing or popping because we never re-randomize after the page loads.
Safe for Static and Dynamic Pages:
This Server Component works whether you use it in a static route (/home
) or a dynamic MDX route (/[slug]
).
Fonts naturally have invisible ascent and descent padding — meaning large text can have unexpected whitespace above and below.
To control this:
m-0 p-0
.leading-[1]
to tighten the line height.-mt-0 -mb-3
) and padding (pt-3
) to manually crop extra whitespace.This keeps the hero text looking strong and balanced no matter which font is chosen.
Adding a random font on page load is a small detail, but it brings a sense of life to a design.
By handling randomness purely server-side and tuning layout carefully,
we can achieve this with no performance trade-offs and no visual instability.
This Server Component approach in Next.js is clean, fast, and production-ready.