tanstack-start-typekit
v0.0.1
Published
TanStack Start font loading for Google Fonts and local font files.
Maintainers
Readme
tanstack-start-typekit brings a small font API to TanStack Start: import
Google helpers or local font helpers, register fonts during each SSR request,
and let request middleware inject the generated @font-face rules into the
HTML response.
This is an experimental community package and is not affiliated with TanStack.
Features
- Google Font factory for any Google Fonts family name
- No hardcoded Google font allowlist
- Local font support for one file, many weights, or variable font families
- Generated class names and optional CSS variable classes
- Browser-safe client modules so React hydration keeps the same class names
- Google Font CSS cached in
node_modules/.cache/tanstack-start-typekit - Google font strategies: keep font files hosted by Google or inline them
- Optional selector rules for app-wide font application
Font registration is request-scoped. For app-wide fonts, call your font helpers
from the function passed to createFontMiddleware(). For route-specific fonts,
call the helper during SSR render. Avoid relying on module-level font calls for
CSS injection, because those can run outside the request collector.
Runtime Support
This package is designed for TanStack Start apps running with Node-compatible
SSR runtimes such as Node.js or Bun. The server modules use Node APIs including
node:fs/promises, node:async_hooks, Buffer, and process.env.
Edge and worker runtimes, including Cloudflare Workers, are not guaranteed unless your deployment provides compatible Node APIs. For those targets, prefer public font URLs or plain CSS until you verify the runtime behavior in your app.
Install
npm install tanstack-start-typekitAdd The Vite Plugin
// vite.config.ts
import { tanstackStart } from "@tanstack/react-start/plugin/vite";
import viteReact from "@vitejs/plugin-react";
import { defineConfig } from "vite";
import font from "tanstack-start-typekit";
export default defineConfig({
plugins: [
font(),
tanstackStart(),
viteReact(),
],
});The plugin configures browser-safe font modules and rewrites static relative
local font paths to Vite ?url imports.
The local font rewrite is intentionally small and supports static src and
path values inside localFont(...) calls. Dynamic font paths should use public
URLs or be handled manually.
If you want framework-style imports, enable aliases with
font({ includeFrameworkAliases: true }) and import from tanstack/font/google
or tanstack/font/local. The package subpaths above are the default because
they work with TypeScript without extra configuration.
Add The Request Middleware
// src/start.ts
import { createStart } from "@tanstack/react-start";
import { createFontMiddleware } from "tanstack-start-typekit/middleware";
import { registerFonts } from "./config/fonts";
export const startInstance = createStart(() => ({
requestMiddleware: [createFontMiddleware(registerFonts)],
}));The middleware wraps each request, collects font CSS registered during SSR, and
injects the final style tag and local font preload links into HTML responses.
Passing your app font registration function to createFontMiddleware() keeps
global font registration inside the same request-scoped collector as the HTML
injection step, which is useful when the app shell renders through server
functions or cached document responses.
// src/config/fonts.ts
import googleFont from "tanstack-start-typekit/google";
import localFont from "tanstack-start-typekit/local";
const Cairo = googleFont("Cairo");
export function registerFonts() {
Cairo({
subsets: ["arabic", "latin"],
weight: ["400", "700"],
variable: "--font-cairo",
fallback: ["Tahoma", "Arial", "sans-serif"],
selector: ["html[lang='ar']", "body[dir='rtl']", ".font-cairo"],
variableSelector: ":root",
});
localFont({
family: "Brand Sans",
src: "/fonts/brand-sans.woff2",
fallback: ["system-ui", "sans-serif"],
preload: true,
selector: ".font-brand",
});
}Use A Google Font
import googleFont from "tanstack-start-typekit/google";
const Inter = googleFont("Inter");
export function Page() {
const inter = Inter({
subsets: ["latin"],
weight: ["400", "700"],
display: "swap",
strategy: "external",
variable: "--font-inter",
fallback: ["system-ui", "sans-serif"],
});
return (
<main className={`${inter.className} ${inter.variable}`}>
<h1>TanStack Start pages with crisp, optimized type.</h1>
</main>
);
}By default, Google font CSS keeps its fonts.gstatic.com URLs intact, so the
browser loads font files from Google at runtime. To inline Google font files into
the generated CSS, opt in per font:
const inter = Inter({
weight: ["400", "700"],
strategy: "inline",
});Need a helper that is not exported yet?
import { createGoogleFont } from "tanstack-start-typekit/google";
const DM_Sans = createGoogleFont("DM Sans");Use A Local Font
import localFont from "tanstack-start-typekit/local";
export function Heading() {
const brand = localFont({
src: [
{ path: "../fonts/Brand-Regular.woff2", weight: "400", style: "normal" },
{ path: "../fonts/Brand-Bold.woff2", weight: "700", style: "normal" },
],
display: "swap",
variable: "--font-brand",
fallback: ["system-ui", "sans-serif"],
});
return <h1 className={brand.className}>Brand heading</h1>;
}Static relative local paths in TypeScript and JavaScript files are rewritten to
Vite ?url imports so Start can fingerprint and emit the files. Public URLs
such as /fonts/Brand.woff2 are kept as URLs. Local font URLs are preloaded by
default unless you pass preload: false.
Returned Object
Both font modules return the same shape:
type StartFont = {
className: string;
variable?: string;
family: string;
style: {
fontFamily: string;
fontStyle?: string;
fontWeight?: string;
};
};Use className on React elements, apply variable to expose a CSS custom
property, read family for a resolved font-family value, or read style when
you need inline style values.
