# How to Use the `font!` Macro and Fontsource for Web Fonts in Topcoat

> Learn to use the font! macro and Fontsource for web fonts in Topcoat. Effortlessly serve custom fonts with automatic router discovery and the topcoat::font::link component.

- Repository: [Tokio/topcoat](https://github.com/tokio-rs/topcoat)
- Tags: how-to-guide
- Published: 2026-07-31

---

**Topcoat's `font!` and `fontsource_font!` macros compile web font declarations into `Font` constants that the router automatically discovers and serves via the `topcoat::font::link` component.**

The `tokio-rs/topcoat` framework provides first-class support for web fonts through the `topcoat-font` crate. By using the `font!` macro for custom `@font-face` rules or the `fontsource_font!` macro for the Fontsource catalog, you can embed type-safe font definitions directly in your Rust code and serve them with content-hashed URLs.

## Declaring Custom Fonts with the `font!` Macro

The `font!` macro, defined in [`crates/topcoat-font/src/font.rs`](https://github.com/tokio-rs/topcoat/blob/main/crates/topcoat-font/src/font.rs), allows you to write raw `@font-face` CSS rules in Rust. It automatically injects the family name into every rule and produces a `Font` constant that the router can register.

To declare a custom font, wrap your `@font-face` blocks inside the macro, specifying the family name as the first argument:

```rust
use topcoat::{
    font::{Font, font},
    router::{Router, RouterBuilderDiscoverExt},
    view::view,
};

/// Declare the "Orbitron" font manually.
const ORBITRON: Font = font! {
    "Orbitron",
    @font-face {
        src: url(
            "https://cdn.jsdelivr.net/fontsource/fonts/orbitron:vf@latest/latin-wght-normal.woff2"
        ) format("woff2") tech("variations");
        font-weight: 100 900;
        font-display: swap;
    }
};

```

The macro validates the syntax at compile time and generates a `Font` struct containing the family name and a vector of `FontFace` definitions. You can then register this constant automatically using the router's `.discover()` method, which scans for font constants annotated with `#[font]`:

```rust
#[tokio::main]
async fn main() {
    // `.discover()` picks up `ORBITRON` automatically.
    let router = Router::builder().discover().build();
    topcoat::start(router).await.unwrap();
}

```

## Using Fontsource with the `fontsource_font!` Macro

For fonts available in the Fontsource catalog, use the `fontsource_font!` macro implemented in [`crates/topcoat-font/src/fontsource.rs`](https://github.com/tokio-rs/topcoat/blob/main/crates/topcoat-font/src/fontsource.rs). This macro validates the requested family, weights, styles, and subsets at compile time against the built-in `fontsource::families` module.

First, enable the **`font-fontsource`** feature in your [`Cargo.toml`](https://github.com/tokio-rs/topcoat/blob/main/Cargo.toml):

```toml
[dependencies]
topcoat = { version = "0.5", features = ["font-fontsource"] }

```

Then declare your font using the family name:

```rust
#[cfg(feature = "font-fontsource")]
use topcoat::font::{Font, fontsource::fontsource_font};

#[cfg(feature = "font-fontsource")]
const ROBOTO: Font = fontsource_font!(ROBOTO);

```

The macro generates the same `Font` struct as `font!`, but automatically assembles the required file URLs from the Fontsource registry. The constant can be registered and linked exactly like custom fonts declared with `font!`.

## Configuring Weights, Styles, and Subsets

Both macros support selective inclusion of font variants to reduce bundle size. With `fontsource_font!`, you can specify exactly which weights, styles, and character subsets to include:

```rust
#[cfg(feature = "font-fontsource")]
const ROBOTO: Font = fontsource_font!(
    ROBOTO,
    weight: [400, 700],
    style: Normal,
    subset: [Latin, Cyrillic],
);

```

Only the requested files are included in the generated `Font` struct. The macro performs compile-time validation to ensure the requested combinations exist in the Fontsource catalog, preventing runtime 404 errors.

## Self-Hosting Fonts with the Asset System

By default, Fontsource fonts load from CDNs. To serve them from your own domain with content-hashed URLs, use Topcoat's asset integration. In [`crates/topcoat-font/src/fontsource.rs`](https://github.com/tokio-rs/topcoat/blob/main/crates/topcoat-font/src/fontsource.rs), the macro supports a `host: Asset` parameter that triggers build-time downloading.

When you wrap URLs with `url(asset!(…))` or supply `host: Asset` to `fontsource_font!`, the `topcoat-asset` crate downloads the font files during compilation and registers them in the `AssetBundle`:

```rust
#[cfg(feature = "font-fontsource")]
use topcoat::{
    asset::{AssetBundle, RouterBuilderAssetExt},
    font::{Font, fontsource::fontsource_font},
    router::{Router, RouterBuilderDiscoverExt},
};

#[cfg(feature = "font-fontsource")]
const ROBOTO: Font = fontsource_font!(ROBOTO, host: Asset);

#[cfg(feature = "font-fontsource")]
fn build_router() -> Router {
    Router::builder()
        .assets(AssetBundle::load().unwrap()) // serves the downloaded fonts
        .discover()
        .build()
}

```

The router then serves the font files from your origin, eliminating external CDN dependencies and improving cache control.

## Rendering Fonts in Views

Once registered, reference your font in a `view!` block using `topcoat::font::link`. This component injects a `<link rel="stylesheet">` containing the generated `@font-face` blocks into your page's `<head>`:

```rust
#[page("/")]
async fn home() -> topcoat::Result {
    view! {
        <!DOCTYPE html>
        <html>
            <head>
                topcoat::font::link(font: ORBITRON)
            </head>
            <body>
                <h1 style="font-family: 'Orbitron'">"Hello!"</h1>
            </body>
        </html>
    }
}

```

The generated HTML contains the exact `@font-face` rules defined in your `font!` or `fontsource_font!` declarations, making the font available to any CSS on the page via the `font-family` property.

## Summary

- The **`font!`** macro in [`crates/topcoat-font/src/font.rs`](https://github.com/tokio-rs/topcoat/blob/main/crates/topcoat-font/src/font.rs) compiles custom `@font-face` rules into type-safe `Font` constants.
- The **`fontsource_font!`** macro in [`crates/topcoat-font/src/fontsource.rs`](https://github.com/tokio-rs/topcoat/blob/main/crates/topcoat-font/src/fontsource.rs) pulls validated fonts from the Fontsource catalog at compile time.
- Enable the **`font-fontsource`** Cargo feature to access Fontsource integration.
- Use **`host: Asset`** with `fontsource_font!` to download and self-host font files with content-hashed URLs.
- Register fonts automatically via **`Router::builder().discover()`** or manually with `.font(...)`.
- Render font stylesheets in views using **`topcoat::font::link(font: CONSTANT)`**.

## Frequently Asked Questions

### What is the difference between `font!` and `fontsource_font!`?

The **`font!`** macro accepts raw CSS `@font-face` blocks and is ideal for custom fonts or CDN URLs you define manually. The **`fontsource_font!`** macro queries the Fontsource catalog at compile time, validating that the family exists and generating the correct URLs automatically. Both produce a `Font` struct that integrates identically with the router and view system.

### How do I self-host Fontsource fonts instead of using a CDN?

Add the **`host: Asset`** parameter to your `fontsource_font!` invocation, as implemented in [`crates/topcoat-font/src/fontsource.rs`](https://github.com/tokio-rs/topcoat/blob/main/crates/topcoat-font/src/fontsource.rs). Then load an `AssetBundle` in your router with `.assets(AssetBundle::load().unwrap())`. The build script downloads the font files, hashes them for cache-busting, and serves them from your origin.

### Can I use variable fonts or multiple weights with these macros?

Yes. With `font!`, specify variable font ranges in the `font-weight` property (e.g., `100 900`). With `fontsource_font!`, pass an array to the `weight:` parameter (e.g., `weight: [400, 700]`). The macros include only the specified variants, optimizing your bundle size.

### Why does my font constant need `#[cfg(feature = "font-fontsource")]`?

The `fontsource_font!` macro and its associated imports are only available when the **`font-fontsource`** feature is enabled in your [`Cargo.toml`](https://github.com/tokio-rs/topcoat/blob/main/Cargo.toml). The conditional compilation prevents compilation errors if the feature is disabled, ensuring your code remains portable across different build configurations.