Nib supports loading custom font files for use in your views. Fonts can be auto-detected from the assets directory, registered manually by path, or loaded from a URL.
Auto-detection from assets¶
The simplest approach is to place font files in your assets/ directory. Nib scans for font files automatically before the first render:
myapp/
├── src/
│ ├── main.py
│ └── assets/
│ ├── Geist-Regular.ttf
│ ├── Geist-Bold.ttf
│ └── FiraCode-Regular.otf
Font files are detected by extension. Supported formats:
| Extension | Format |
|---|---|
.ttf |
TrueType |
.otf |
OpenType |
.ttc |
TrueType Collection |
.woff |
Web Open Font Format |
.woff2 |
Web Open Font Format 2 |
Font naming¶
The font name is derived from the filename without its extension. For example:
| Filename | Font name |
|---|---|
Geist-Regular.ttf |
Geist-Regular |
FiraCode-Medium.otf |
FiraCode-Medium |
Inter-Bold.ttf |
Inter-Bold |
Subdirectory scanning¶
Nib scans the assets directory recursively, so you can organize fonts in subdirectories:
assets/
├── fonts/
│ ├── Geist-Regular.ttf
│ └── Geist-Bold.ttf
└── images/
└── logo.png
Both fonts will be detected and available by their filename-based names.
Manual registration¶
For fonts outside the assets directory, register them explicitly with app.fonts:
app.fonts = {
"MyCustomFont": "/absolute/path/to/MyCustomFont.ttf",
"AnotherFont": "/Users/me/Library/Fonts/AnotherFont.otf",
}
The dictionary maps font names (used in code) to absolute file paths.
URL-based fonts¶
You can also load fonts from a URL:
app.fonts = {
"WebFont": "https://example.com/fonts/WebFont.ttf",
}
The Swift runtime downloads and caches the font at startup.
Combining auto-detection and manual registration¶
Manual registrations are merged with auto-detected fonts. If there is a name conflict, the manual registration takes precedence:
# Auto-detected fonts from assets/ are available automatically.
# These manual entries are added on top:
app.fonts = {
"Geist-Regular": "/path/to/override/Geist-Regular.ttf", # overrides auto-detected
"ExternalFont": "/other/path/ExternalFont.otf", # new font
}
Using custom fonts¶
Once registered (automatically or manually), use nib.Font.custom() to reference a custom font:
nib.Text("Hello, World!", font=nib.Font.custom("Geist-Regular", size=16))
The first argument is the font name (matching the registered name), and size is the point size.
In different views¶
Custom fonts work anywhere a font parameter is accepted:
# Text
nib.Text("Custom styled text", font=nib.Font.custom("FiraCode-Regular", size=14))
# TextField
nib.TextField(value="", placeholder="Type here", font=nib.Font.custom("Inter-Bold", size=14))
# Button
nib.Button(
content=nib.Text("Click Me", font=nib.Font.custom("Geist-Bold", size=16)),
action=on_click,
)
On Canvas¶
Custom fonts work with the nib.draw.Text command as well:
canvas.draw([
nib.draw.Text(
"Canvas text",
x=10, y=50,
font=nib.Font.custom("Geist-Regular", size=20),
fill="#ffffff",
),
])
Font loading order¶
Font discovery and registration happens in this order:
- Nib auto-detects font files in the
assets/directory (recursively). - Manually registered fonts via
app.fontsare merged in, overriding any auto-detected fonts with the same name. - The combined font dictionary is sent to the Swift runtime on the first render.
- Swift loads and registers all fonts before displaying the UI.
Note
Font loading happens once, before the first render. If you update app.fonts after the app is running, the new fonts will be sent on the next re-render.
Complete example¶
import nib
def main(app: nib.App):
app.title = "Fonts"
app.icon = nib.SFSymbol("textformat")
app.width = 350
app.height = 350
# Manual registration (auto-detection from assets/ also works)
app.fonts = {
"Geist-Regular": "/Users/me/fonts/Geist-Regular.ttf",
"Geist-Bold": "/Users/me/fonts/Geist-Bold.ttf",
}
app.build(
nib.VStack(
controls=[
nib.Text("System Font", font=nib.Font.TITLE),
nib.Divider(),
nib.Text(
"Custom Regular",
font=nib.Font.custom("Geist-Regular", size=18),
),
nib.Text(
"Custom Bold",
font=nib.Font.custom("Geist-Bold", size=18),
),
nib.Divider(),
nib.Text(
"Small custom font",
font=nib.Font.custom("Geist-Regular", size=12),
foreground_color=nib.Color.GRAY,
),
nib.Text(
"Large custom font",
font=nib.Font.custom("Geist-Bold", size=28),
),
],
spacing=12,
padding=24,
)
)
nib.run(main)
Tip
For the best developer experience, place font files in assets/ and let auto-detection handle everything. You only need manual registration for fonts stored outside your project directory or loaded from URLs.