Back to MakeFont

Guide

How to use a custom font in Lovable, v0 and Bolt

Add your own font files (WOFF2) to a Lovable, v0 or Bolt project: where the files go, @font-face or next/font/local, Tailwind v3 and v4 setup, prompts that work, and how to check it.

9 min read · Updated 8 September 2026

Type a font name into Lovable, v0 or Bolt and the answer is usually a Google Font. Handy, sure, but it is also why so many AI-built sites look alike. If you have font files of your own, maybe a brand typeface, something you licensed, or a design made just for your project, you will not find a font picker anywhere. Instead you place the files in the project yourself and give the agent precise instructions on how to use them. Below you will see how that works in each tool, with ready-to-use code for plain CSS, Tailwind v3, Tailwind v4 and Next.js, plus prompts you can paste.

What to sort out before you start

  • Use WOFF2. The format is compressed and every current browser understands it. Have only TTF or OTF at hand? Convert them first, for instance with fonttools (see our self-hosting guide).
  • Know your weights. Pick either a single variable font covering a range of weights, or separate static files for each weight and style (brand-sans-400.woff2, brand-sans-700.woff2).
  • Use simple file names. Stick to lowercase and skip spaces. On most hosting servers Brand-Sans.woff2 and brand-sans.woff2 count as two different files.
  • Check the license permits use on the web.

Throughout the examples below the font goes by Brand Sans. Swap in whatever yours is called.

What each tool generates

Lovable. Projects created since May 13, 2026 run on TanStack Start, a React framework that renders pages on the server and builds through Vite. Anything older is React plus Vite. Tailwind CSS handles the styling in both cases. Not sure which one you have? Just ask Lovable: "What stack is this project on?"

v0. Every new chat opens as a Next.js app built on React, TypeScript, Tailwind CSS and shadcn/ui.

Bolt. Bolt produces JavaScript apps and works with any frontend framework, so look up which one your project runs on. Open package.json in Code view: seeing vite means the Vite route below applies, next means the Next.js route.

Whichever tool you use, public is the folder for static files. A file placed in public/fonts/ gets served under /fonts/, which is why your CSS points to /fonts/brand-sans-400.woff2 and never mentions public.

Step 1: Bring the font files into the project

In v0, drop the files right into the chat as attachments. WOFF, WOFF2 and TTF uploads are accepted, with a per-file limit of 5 MB on the free plan and 20 MB on paid plans. Drag them into the conversation or click the plus icon and choose Upload from computer. In that same message, mention where they should go (the prompts below show how). When your v0 project is linked to GitHub, committing the files to the repository works as well.

In Bolt, click the <> icon to enter Code view, open the public folder and drag your files in. Want them under public/fonts/? Ask Bolt to move them there, or simply adjust the paths used below. Bolt's documentation describes this flow for images; it stores any file directly inside your project and does not spend tokens on it. Font formats do not appear in the chat attachment list, which makes Code view the dependable option. Keep in mind that Code view is read-only in Safari and missing on mobile, so reach for a Chromium browser on a desktop machine. A synced GitHub repository is another route: Bolt picks up changes that were made outside Bolt.

In Lovable, the documentation says it plainly: there is no dedicated upload feature for font files, though you can place them in your repository via Git sync and then ask Lovable to use them. In the project settings, connect the project to GitHub (GitLab and Bitbucket work too). Next, on GitHub, open the repository, navigate to the public folder, choose Add file and then Upload files, drop your WOFF2 files in, and prefix each file name with fonts/ if you want a subfolder. Commit to the synced branch and the files show up inside your Lovable project. Lovable has also said in the past that font files attached in the chat can become a custom font; should you try that, verify afterwards that the files truly landed in public/fonts/.

Step 2: Declare the font (Lovable, Bolt and other Vite projects)

Put @font-face rules into your main CSS file. Older Lovable projects keep that at src/index.css; otherwise it is whichever file opens with the Tailwind import.

@font-face {
  font-family: "Brand Sans";
  src: url("/fonts/brand-sans-400.woff2") format("woff2");
  font-weight: 400;
  font-style: normal;
  font-display: swap;
}
@font-face {
  font-family: "Brand Sans";
  src: url("/fonts/brand-sans-700.woff2") format("woff2");
  font-weight: 700;
  font-style: normal;
  font-display: swap;
}

Working with a variable font? A single rule with the weight range does it, for example font-weight: 100 900;.

Step 3: Wire the font into Tailwind

Start by figuring out which Tailwind version you have. Spotting @import "tailwindcss"; in your CSS means v4. A tailwind.config.js or tailwind.config.ts together with @tailwind base; in the CSS means v3.

Tailwind v4: declare the font inside the CSS file, right under the import:

@theme {
  --font-sans: "Brand Sans", system-ui, sans-serif;
  --font-display: "Brand Sans", system-ui, sans-serif;
}

--font-sans turns it into the default text font, while --font-display hands you a font-display class meant for headings only. When your CSS already defines --font-sans inside a @theme inline block (typical for shadcn/ui setups), edit it there rather than adding a second one.

Tailwind v3: put it in the config file:

theme: {
  extend: {
    fontFamily: {
      sans: ['"Brand Sans"', "system-ui", "sans-serif"],
      display: ['"Brand Sans"', "system-ui", "sans-serif"],
    },
  },
},

On its own, Tailwind v4 no longer reads tailwind.config.js. So when an agent writes a font into that file inside a v4 project, the result is: nothing happens.

v0 and other Next.js projects

Next.js ships its own loader for local files called next/font/local. It self-hosts the font, preloads it and builds a size-matched fallback, so the page stays put when the font arrives. The path is resolved relative to the file calling localFont, which makes app/fonts/ beside your layout the easiest home for the files. (public/fonts/ is fine too, using ../public/fonts/... as the path.)

// app/layout.tsx
import localFont from "next/font/local";

const brandSans = localFont({
  src: [
    { path: "./fonts/brand-sans-400.woff2", weight: "400", style: "normal" },
    { path: "./fonts/brand-sans-700.woff2", weight: "700", style: "normal" },
  ],
  display: "swap",
  variable: "--font-brand",
});

export default function RootLayout({ children }: { children: React.ReactNode }) {
  return (
    <html lang="en" className={brandSans.variable}>
      <body className="font-sans">{children}</body>
    </html>
  );
}

After that, point Tailwind at the variable. With v4, in app/globals.css:

@theme inline {
  --font-sans: var(--font-brand);
}

With v3, set fontFamily: { sans: ["var(--font-brand)"] } in the config. Should app/layout.tsx already pull in a font through next/font/google, take it out, otherwise it keeps competing with yours.

Prompts to give your tool

Spell out the details: file names, folder, which text should carry the font, and that the previous font has to go. In v0, attach the files and write:

Prompt for your agent

Add the attached font files as a local font: save them in app/fonts, load them with next/font/local in app/layout.tsx with the CSS variable --font-brand, map Tailwind's font-sans to it, remove any other next/font import, and use it for all text.

In Lovable, once the files sit in the repository:

Prompt for your agent

The font files in public/fonts are our brand font Brand Sans (400 and 700). Declare them with @font-face and font-display swap in the main CSS file, set Tailwind's sans font to Brand Sans with a system-ui fallback, remove any Google Fonts link or import, and use it for all headings and body text.

In Bolt, after you have dropped the files into public/fonts via Code view, the Lovable prompt works word for word. Want the font on headlines only? Then say "use it only for h1 and h2 through a font-display class" instead.

Make the font choice stick

Agents have short memories. The next page they generate may quietly return to a default font. So write the decision down somewhere the tool reads on every run:

  • Lovable: put a line into your project knowledge, something like "All text uses Brand Sans from public/fonts. Never add Google Fonts."
  • Bolt: add that same line under Knowledge in the project settings.
  • v0: store it as a custom Instruction (plus icon in the prompt bar, Instructions, New Instruction) and switch it on in the chat.

How to check that it works

Checking is simplest when you open the preview or published URL in its own browser tab.

  1. Open the developer tools and switch to the Network tab. Reload the page and filter for woff2. Your files ought to show up with status 200. Seeing a 404 means the path or file name is off.
  2. In Chrome, click a heading in the Elements panel, open Computed and scroll all the way down. Rendered Fonts tells you which font is actually in use. When it reads Arial or some other system font, yours never loaded. Firefox shows the same information in the Inspector's Fonts tab.
  3. Append the font URL (/fonts/brand-sans-400.woff2) to your domain. The browser ought to download the file rather than display an error page.
  4. Look at bold text. A smeared look means the bold file is missing and the browser is faking it.

Common mistakes to watch for

  • **/public in the URL.** The correct form is /fonts/brand-sans-400.woff2, never /public/fonts/....
  • Files attached but not saved. An attachment can survive as context only. Confirm in the code view that the files really exist in the project.
  • Wrong Tailwind version. Editing the config in a v4 project, or using @theme in a v3 project, changes nothing.
  • Circular variables in Next.js. Naming variable: "--font-sans" in localFont and then writing --font-sans: var(--font-sans) in @theme inline makes the variable point at itself. Pick a separate name such as --font-brand.
  • An old Google Font wins. A forgotten <link>, @import or next/font/google import still sets the font on body. Search the whole project for fonts.googleapis.com and next/font/google.
  • Editing blocked. Lovable's code editor is read-only on the free plan, v0's code viewer is read-only, and Bolt's Code view is read-only in Safari. When that happens, have the agent apply the change.

Questions people ask about custom fonts

Can I upload a font file directly to Lovable? Lovable's docs say there is no dedicated upload feature for font files. The documented route is adding the files to your synced Git repository and asking Lovable to use them.

Does v0 support custom fonts? Yes. WOFF, WOFF2 and TTF files are accepted as chat attachments, and the Next.js app v0 generates can load them through next/font/local.

Should I use @font-face or next/font/local? With Next.js, go for next/font/local, since preloading and the fallback are handled for you. In Vite projects, like Lovable and most Bolt apps, use @font-face in your CSS.

Will this survive when the agent regenerates a page? In most cases yes, since the font lives in your CSS and config. Project knowledge or instructions stop new pages from drifting back.

A typeface no other app has

The moment your project loads its own font files, the font can be anything you hold the rights to use, even one created for this project alone. That is exactly what we are building at MakeFont: your agent designs a typeface for your project and delivers it as WOFF2 files you add just as shown above. It is in development, and early access is open. You can read more in What is MakeFont.