nowo-tech/twig-inspector-bundle
Twig Inspector Bundle adds a debugging toolbar to Symfony apps that reveals which Twig template rendered each part of a page. Hover over HTML elements to see template names, blocks, and file paths, making it easier to locate and fix view code during development.
Install it as a development-only dependency:
composer require --dev nowo-tech/twig-inspector-bundle
Register the bundle manually (Symfony Flex may not have a recipe for a package this small) in config/bundles.php:
return [
// ...
Nowo\TwigInspectorBundle\TwigInspectorBundle::class => ['dev' => true],
];
The ['dev' => true] part matters: this bundle annotates rendered HTML so you can hover an element and see which Twig template and which {% block %} produced it. It must never be enabled on a production kernel.
Where to look first, in order:
README.md in the repo — it is the only reliable source for the actual config key prefix and the toolbar toggle (shortcut/button). Do not assume the prefix matches the bundle name.src/DependencyInjection/Configuration.php — confirms the real config root node and every available option. Read this before writing any YAML.src/Resources/views/ — the toolbar/overlay template; this is where you go if the widget renders but looks broken.bin/console debug:container --tag=twig.extension and bin/console debug:twig — confirm the Twig extension and any exposed functions/filters actually registered.First use case: you are handed a screenshot of a page region and need the file. Open any dev page in the browser, activate the inspector, click the region, and read back the template path plus block name. That single loop — "element on screen → file on disk" — is the entire value proposition; everything else is polish.
The click-to-locate loop. Treat it as your first move during any template refactor or bug triage. Before grepping for a CSS class or a translation key, click the element and let the inspector name the template. Then open that file directly. This is meaningfully faster than grep -r "some-copy-string" templates/ when copy comes from translations or is assembled from several partials.
Refactor / dead-code sweeps. When consolidating partials, walk the page with the inspector open and note which includes are actually reachable. Blocks that the inspector never reports are candidates for deletion. Confirm with bin/console debug:twig and a fresh cache clear before deleting — an inspector miss is not proof of unreachability.
Design-handoff mapping. For each region in a design, record template:block in the ticket. This makes review conversations concrete ("the hero is _hero.html.twig block content") and gives the next developer a starting point without running the app themselves.
Keeping it out of the way. Wire the bundle env-gated (['dev' => true]), and add a project-local config file so options live in version control rather than in someone's head:
# config/packages/dev/twig_inspector.yaml
# verify the root key against src/DependencyInjection/Configuration.php
twig_inspector:
enabled: true
If the bundle supports exclusions (paths, blocks, or templates), use them to silence vendor bundles and admin themes — otherwise the overlay gets noisy on every page and people stop using it.
Front-end toolchains. The bundle only touches server-rendered HTML, so it is orthogonal to Vite/Webpack asset pipelines. It works, as long as the markup it annotates survives to the DOM. If a JS framework hydrates or replaces a subtree, the annotations vanish with it. Use the inspector on the server-rendered response (view source, or disable hydration temporarily), not on the hydrated result.
Extending it. Because this is a small single-maintainer package, prefer decorating its services over patching vendor code. Concretely: decorate whatever service produces the annotation attributes if you need a custom naming scheme, and keep any project-specific overrides under src/Twig/ with a compiler pass in src/DependencyInjection/. That way a composer update cannot silently revert your work.
Team onboarding. Put one line in the project README: "install dev deps, run the app, click the toolbar icon, hover anything." This package's whole learning curve is that sentence, and nobody will discover it on their own.
APP_ENV=prod bin/console debug:container | grep -i inspector — it should return nothing.data-* attributes is usually safe, but inside <table>, <select>, <svg>, or <pre> it can produce invalid nesting or visible whitespace. If a layout shifts only when the inspector is active, that is the cause, not your CSS.test env (recommended) or strip the attributes in the test client's response before asserting.bin/console cache:clear (and restart the dev server if you use one). A "the inspector doesn't see my new block" report is almost always a stale cache.{% for %} appears many times; the overlay may show the last or first instance depending on implementation. Do not read ordinal position as meaningful — read the template and block name only.bin/console debug:twig <template> and {% dump %} / dump() if you need certainty. When in doubt, put a temporary unique string in the candidate block and search the page source.z-index and its own font stack; app-level global styles (* { font-family: ... }, aggressive z-index stacking contexts, or overflow: hidden on body) can hide or misplace it. If the icon does not appear, check the console and the computed styles before assuming the bundle is broken.composer.lock, and if you depend on a config key or an extension point in CI, add a smoke test that boots the kernel in dev and asserts the container still has the expected service. That converts a silent breakage into a red build.require-dev discipline. Keep it out of require. If a downstream consumer ever installs your project with --no-dev, the bundle must simply not exist, and nothing in config/ may reference its services unconditionally.How can I help you explore Laravel packages today?