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

Twig Exception Laravel Package

kwrz/twig-exception

Integrates Twig with Laravel's exception handler so errors and debug pages render through Twig templates instead of Blade, keeping error output consistent with your Twig-based views and making custom error pages easy to style.

View on GitHub
Deep Wiki
Context7

Getting Started

Install via Composer alongside your Twig setup:

composer require kwrz/twig-exception

The package is a thin layer: it registers an exception listener that maps an HTTP status code (or a specific route) to a Twig template, renders it, and returns a Response with the correct status. It does not replace your framework's error handling wholesale — it intercepts and gives you a template hook.

First place to look is the package's service provider / registration entry point (for Silex-style apps, a ServiceProviderInterface implementation; for Symfony, a bundle class). That file shows you the constructor options: the Twig environment/service ID, the template path root, and the default fallback template. Read it before the README — for small packages like this, the provider is the documentation.

Minimal wiring, conceptually:

// register the provider, pointing at your error templates
$app->register(new Kwrz\TwigException\Provider(), [
    'twig.exception.path'  => __DIR__ . '/../views/errors',
    'twig.exception.debug' => false,
]);

Then drop in 404.html.twig, 500.html.twig, etc.

First use case: replace the framework's bare "Not Found" text with a branded 404 page. That's the 10-minute win, and it's also the best smoke test that the listener is actually winning the race against the framework's native handler.

Implementation Patterns

Status-code map as the primary contract. The core pattern is a lookup table from status code → template name. Keep that mapping in one config file, not scattered. Your 404 and 500 pages should extend a shared errors/layout.html.twig so the branding lives in one place:

{# errors/404.html.twig #}
{% extends 'errors/layout.html.twig' %}
{% block title %}Page not found{% endblock %}
{% block body %}
    <h1>We couldn't find that page.</h1>
    <p><a href="{{ path('home') }}">Back to the homepage</a></p>
{% endblock %}

Route-specific overrides. The more interesting pattern: some routes deserve a bespoke error page — a checkout flow's 404 should offer "continue shopping," an API-adjacent route should offer docs links. Register those per-route and let the status-code map handle everything else as a fallback. Order matters: route matches should be checked before the generic status-code lookup.

Pass useful context into the template, not the exception. Expose a small, curated variable set (status code, a safe message in production, a correlation/request ID). Do not expose exception.getMessage() or traces on 5xx in production — that's the exact mistake this package makes easy to commit. Tie the message to the debug flag.

Debug vs. production split. In production you want your Twig page; in development you want the full stack trace. Most frameworks swap in Whoops/Flame in debug mode. Verify the package respects that boundary, or gate your registration on debug === false so developers don't lose traces.

Logging and monitoring. The listener should log the exception before rendering. Wire the logger so your error tracker (Sentry, etc.) still receives 5xx events — a common regression when you take over the error handler is silently swallowing failures because the custom page renders "successfully."

Content negotiation. If you serve JSON, check the Accept header (or the route prefix) and return a JSON error body instead of the Twig page. Doing this once, centrally, saves you from a htmlspecialchars-escaped HTML blob inside a JSON API response.

Gotchas and Tips

  • Handler ordering is the whole ballgame. If the framework's own exception handler is registered first and marks the exception as handled, your listener never fires. Register after boot, or confirm the provider sets the right priority. Symptom: your page renders in one environment and not another.
  • Debug mode usually bypasses everything. If you see a stack trace instead of your template, check debug before you check your code.
  • Twig cache. Editing an error template and seeing stale output in prod is almost always the Twig cache, not the package. Clear it as part of any deploy that changes error pages.
  • Status codes with no body. Don't map 204, 304, or HEAD responses to a template — a body on those is a protocol violation and some clients will choke.
  • Recursion. If your error template throws (missing translation, bad path() call for a route that itself 404s), you get either a loop or a blank 500. Keep error templates dependency-light: no DB queries, no dynamic route generation you can't guarantee. Test them by deliberately rendering with a broken context.
  • path() in a 404 page can be a hidden trap if the router itself failed. Prefer hardcoded URLs or a url() to a static route for error-page navigation.
  • Non-HTTP exceptions (console commands, queue workers) should never touch this listener. Confirm it only registers on the HTTP kernel / request cycle.
  • Maturity risk is real. Zero stars and zero dependents means no community regression coverage. Pin the version exactly in composer.json, read the provider source end-to-end (it's small), and be prepared to replace it with ~40 lines of your own listener if it goes unmaintained. The abstraction is thin enough that this is a cheap exit.
  • Extension points to look for: a custom template resolver (so templates can be theme-switched per tenant/locale) and a pre-render event/hook for injecting analytics or correlation IDs. If neither exists, subclass the listener rather than forking.
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.
oxygen/data
oxygen/core
jengo/base
xiloryx/log-collector
a21ns1g4ts/filament-stripe
la-php/laravel-admin
artisan-build/community-prompts
vleroy/smart
ayles-software/laravel-australian-public-holidays
cboxdk/laravel-id-spatie
cboxdk/laravel-id-billing
cboxdk/laravel-id-billing-bridge
simple-as-fuck/php-performance-log
hsluv/hsluv
seampact/codegen
seampact/hooks
seampact/ir
mehedimi/laravel-amp-mail
happones/laravel-vue-i18n-generator
jwerd/inertia-table