Weave Code
Weaver
Helps Laravel developers discover, compare, and choose open-source packages. See popularity, security, maintainers, and scores at a glance to make better decisions.
Feedback
Share your thoughts, report bugs, or suggest improvements.
Subject
Message

Laravel Bunny Fonts Laravel Package

ryangjchandler/laravel-bunny-fonts

Laravel integration for Bunny Fonts, the privacy-friendly Google Fonts alternative. Add Bunny Fonts stylesheets to your Blade views and register font families from a single config file, with no Google tracking and no cookie banners.

View on GitHub
Deep Wiki
Context7

Getting Started

ryangjchandler/laravel-bunny-fonts lets you serve Bunny Fonts (a Google Fonts–API-compatible, privacy-friendly CDN) from your own domain instead of relying on a third-party request from the visitor's browser. v2.1.1 is a maintenance release that adds Laravel 13 support on top of 2.1.0 — no API changes, no deprecations.

Minimum steps:

composer require ryangjchandler/laravel-bunny-fonts
php artisan vendor:publish --tag="bunny-fonts-config"

Look here first:

  1. config/bunny-fonts.php — the only real knobs are where downloaded font files and the generated stylesheet live (typically under public/). Read it before touching anything else; the defaults assume a writable public/ directory.
  2. The README usage block — the package is a thin wrapper around the Google/Bunny Fonts CSS API, so the "API" you learn is the query-string vocabulary (family, weight, display, text).
  3. The facade RyanChandler\BunnyFonts\Facades\BunnyFonts — that's where the fluent builder lives.

First use case, in your layout <head>:

{{ BunnyFonts::font('Inter')->weights([400, 600, 700])->display('swap')->load() }}

That call resolves any not-yet-downloaded files, writes them to the configured public path, and emits the markup referencing your own domain. Drop it above your own stylesheet so your CSS overrides win.

Implementation Patterns

Treat the stylesheet as a deploy artifact. The package downloads fonts lazily on first render. The pattern that works in production is: render your layout once during deploy (a php artisan tinker one-liner, a warm-up route, or a smoke-test request), then either commit the generated files or bake them into the image. After that, request-time rendering is a no-op read.

Centralise in a layout/component, never call it per-view. Build one Blade component (<x-fonts /> or a partial included from your base layout) so there's exactly one place families are declared and one place to bump weights. Scattering BunnyFonts:: calls across views makes cache invalidation guesswork.

One call per family, ordered by criticality. Chain weights() for the full range you actually use, and pick display('swap') for body text and display('optional') for decorative faces. Requesting weights you don't use in CSS is pure waste — the CSS API returns a @font-face block per weight and the downloader fetches every one.

Local development via a warm cache. Since the first hit does network I/O against fonts.bunny.net, either accept the one-off latency or pre-render the stylesheet as part of your composer setup/seed script. In Docker, mount or chown the fonts directory; a read-only public/ fails silently into an unstyled page if you're not watching logs.

Tests. Because fetching goes through Laravel's HTTP client, Http::fake() with a canned Google-format CSS response is the cheapest way to assert your layout renders the right <link> without hitting the network. Faking is preferable to pre-warming here — you also get to test the fallback path when a family is misspelled.

Multi-tenant / multi-site. Point the config at a per-tenant subdirectory (e.g. public/fonts/{tenant}) and keep a small map of family → directory. The generated CSS path becomes cache-key material; don't let it be user-controllable.

Upgrading across v2.x. v2.1.1 is drop-in from 2.1.0. The only actionable item is the constraint: if you're on Laravel 13, require ^2.1.1 explicitly — ^2.1.0 would resolve fine today but pin your reasoning; if you're below Laravel 10, this major line isn't for you.

Gotchas and Tips

  • Runtime side effects on a normal request. The largest pitfall is that the first page view performs outbound HTTP and disk writes. Under a cold cache and concurrent traffic, several requests can race to fetch the same file. Warm it; don't discover it under load.
  • Silent failures. A blocked outbound connection to fonts.bunny.net (proxy, firewall, no DNS in a build container) or an unwritable directory typically degrades to a missing/empty stylesheet rather than an exception. Always eyeball the rendered <head> and the fonts directory on a new environment before blaming your CSS.
  • config:cache is mandatory after config edits. With a cached config the published paths are frozen; editing config/bunny-fonts.php and seeing no effect is the classic false alarm.
  • Files bypass Vite. Fonts written into public/ aren't fingerprinted or versioned by the Vite manifest. If you serve assets through a CDN, either include the fonts directory in the same bucket/sync or configure long-lived immutable caching on the generated filenames.
  • Don't pass user input to the fluent builder. Family names, weights, and the optional text subset parameter end up in a remote URL and in filesystem paths. Whitelist families in a config array.
  • Bunny's CSS API is Google-compatible but not identical. Unusual weights (e.g. variable-axis requests, ital handling, text= subsetting) can behave differently. If a build looks wrong, curl the equivalent fonts.bunny.net/css2?... URL directly and compare it against what got generated.
  • Clearing public/fonts re-downloads everything on the next request. Include the directory in your deploy artifact/backup and be deliberate about php artisan optimize:clear-adjacent cleanup scripts.
  • Extension points are thin by design. If you need a proxy, a custom HTTP client, or offline builds, bind your own implementation or pre-generate the CSS yourself — don't fork the package for a transport tweak.
  • Scope note for v2.1.1: the accompanying Dependabot bump (fetch-metadata 2.4.0 → 2.5.0) is CI-only. Neither change affects runtime behaviour, so there is nothing to re-test beyond a standard composer update on Laravel 13.
Weaver

How can I help you explore Laravel packages today?

Conversation history is not saved when not logged in.
Prompt
Add packages to context
No packages found.
dakiquang/laravel-oauth-refresh-lock
woda/laravel-worktrees
dalefranks/ramasite
faghy/laracashbook
roundly-consulting/google-places-for-laravel
roundly-consulting/kubernetes-api-for-laravel
roundly-consulting/plausible-for-laravel
gvp/gvp-laravel-template
metasyncsite/laravel-metasync-client
roundly-consulting/onboarding-for-laravel
andydefer/laravel-notification
adachsoft/cli-process-tester
jhaskell13/laraflow
sebastienheyd/boilerplate-media-manager
ccast/tagixo-primix
pretendtrue/laravel-builder
sawmainek/laravel-scout-tntsearch-driver
tomshaw/laravel-calendar-table
dibakar/laravel-ownership
adachsoft/changelog-linter