Appearance
Header & Footer
Code snippets — analytics, tag managers, pixels, verification tags — on every page of the site.
kitloom/header-footer · admins: kitloom/header-footer-filament, kitloom/header-footer-nova
Video lessons
Recorded in the Filament admin; the Nova admin works the same way.
Puts code you paste into every page of the site: analytics, a tag manager, an ad pixel, a chat widget, a site-verification tag. It is what "Insert Headers and Footers" or WPCode does on a WordPress site. The settings are in the admin under Header & Footer → Settings (kitloom/header-footer-filament or kitloom/header-footer-nova).
Adding a snippet
- Open Header & Footer → Settings and press Add snippet.
- Label: a name for you, such as "Analytics" or "Chat". Visitors never see it.
- Place: where in the page the code goes (see below).
- Code: paste it exactly as the service gives it, with its
<script>,<noscript>or<meta>tags. It goes into the page as it is, unchecked. A snippet with a mistake in it can break the page's layout or its other scripts, so look at the site after saving. - Consent category: what the visitor has to agree to before this code runs (see below).
- On: switch a snippet off to keep it without using it.
- Save Changes.
Snippets go into each place in the order of the list; move a snippet up or down to change it. The Status box at the top counts the active snippets in each place.
Where the code goes
| Place | Where in the page | Typical use |
|---|---|---|
In <head> | just before </head> | Google tag (GA4), Tag Manager's main script, Meta pixel, verification <meta> tags, styles |
Right after <body> | first thing in the page body | Tag Manager's <noscript> part, anything that must be there before the content |
Before </body> | last thing in the page body | chat widgets, scripts that wait for the page to be drawn |
When a service's instructions say "paste into the <head>", choose In <head>; "immediately after the opening <body> tag" is Right after <body>; "before the closing </body> tag" is Before </body>.
The code appears on the pages of the site — the theme's pages, its "not found" page included. It is not added to the admin panels or to the application's own pages.
Consent categories
Each snippet says what it does with the visitor's data, in the categories of the WordPress Consent API that cookie banners use:
| Category | What it is for | Examples |
|---|---|---|
| Functional | the site does not work without it; it always loads | a verification tag, a payment or chat widget the visitor asked for |
| Preferences | remembers the visitor's choices | a language or region switcher of a third-party service |
| Statistics | counts visits and how the site is used | Google Analytics, Plausible, Matomo |
| Marketing | ads, retargeting, tracking across sites | Meta pixel, Google Ads, LinkedIn Insight Tag |
With a consent module installed (kitloom/cookie-consent), snippets of every category but Functional wait until the visitor agrees to that category. Without one, every active snippet loads for every visitor, whatever its category — the status box says which is the case.
Google tag (reads Consent Mode): tick it for a Google tag (gtag.js) or a Tag Manager container that uses Google Consent Mode. Such a tag is meant to load at once and be told what the visitor agreed to, rather than wait; a consent module treats it that way.
Examples
The ids below (G-XXXXXXX, GTM-XXXXXXX, 000000000000000) are placeholders: copy the real code from your account in each service.
Google Analytics 4 — place In <head>, category Statistics, Google tag ticked:
html
<script async src="https://www.googletagmanager.com/gtag/js?id=G-XXXXXXX"></script>
<script>
window.dataLayer = window.dataLayer || [];
function gtag(){dataLayer.push(arguments);}
gtag('js', new Date());
gtag('config', 'G-XXXXXXX');
</script>Google Tag Manager comes in two parts, so it is two snippets. The script — place In <head>:
html
<script>(function(w,d,s,l,i){w[l]=w[l]||[];w[l].push({'gtm.start':
new Date().getTime(),event:'gtm.js'});var f=d.getElementsByTagName(s)[0],
j=d.createElement(s),dl=l!='dataLayer'?'&l='+l:'';j.async=true;j.src=
'https://www.googletagmanager.com/gtm.js?id='+i+dl;f.parentNode.insertBefore(j,f);
})(window,document,'script','dataLayer','GTM-XXXXXXX');</script>and its fallback for browsers without JavaScript — place Right after <body>:
html
<noscript><iframe src="https://www.googletagmanager.com/ns.html?id=GTM-XXXXXXX"
height="0" width="0" style="display:none;visibility:hidden"></iframe></noscript>Give both the category of what the container loads (Marketing when it has ad tags). If the container handles consent itself with Consent Mode, tick Google tag on the script.
Meta pixel — place In <head>, category Marketing:
html
<script>
!function(f,b,e,v,n,t,s){if(f.fbq)return;n=f.fbq=function(){n.callMethod?
n.callMethod.apply(n,arguments):n.queue.push(arguments)};if(!f._fbq)f._fbq=n;
n.push=n;n.loaded=!0;n.version='2.0';n.queue=[];t=b.createElement(e);t.async=!0;
t.src=v;s=b.getElementsByTagName(e)[0];s.parentNode.insertBefore(t,s)}(window,
document,'script','https://connect.facebook.net/en_US/fbevents.js');
fbq('init', '000000000000000');
fbq('track', 'PageView');
</script>A verification tag — place In <head>, category Functional:
html
<meta name="google-site-verification" content="XXXXXXXXXXXXXXXX">(With the SEO module installed, its verification fields do the same for Google, Bing and others.)
The page cache
When the site keeps its pages in a cache for visitors, the snippets are part of each cached page — the same page, the same code, for everyone. Saving the snippets clears the cache, so the next visit to every page brings the new code. For the same reason a snippet cannot hold anything about one visitor: what differs per visitor is decided by the script itself, in the browser.
The theme
The code lands where the theme's main layout marks the three places: @siteHead before </head>, @siteBodyOpen right after <body>, @siteFooter before </body>. Every kitloom theme has them. A theme of your own adds them to its layout — an Inertia theme to its root view, resources/views/app.blade.php. Without them the snippets are saved but appear nowhere; the status box reminds of it.
Themes on Inertia (single-page navigation)
An Inertia theme loads the page once; going to another page swaps the content without loading a new page, so the snippets run once, on the first page. Analytics then sees one page view per visit unless the tool follows the page changes:
- Google Analytics 4: in the data stream's Enhanced measurement, keep "Page changes based on browser history events" on.
- Google Tag Manager: a History Change trigger on the page-view tags.
- Anything else: count a page view on Inertia's
navigateevent, in a snippet placed Before</body>. The address check keeps the first page from being counted twice:
html
<script>
(function () {
var last = location.href;
document.addEventListener('inertia:navigate', function () {
if (location.href === last) { return; }
last = location.href;
fbq('track', 'PageView'); // the tool's own page-view call
});
})();
</script>Who may edit the snippets
Code pasted here runs in the browser of everyone who opens the site, administrators included: it can read what they type and act as them. WordPress keeps this behind its unfiltered_html capability. Here it is the permission Manage code snippets on every page (manage_header_footer), given to administrators when the module is installed. Give it to another role on the roles screen only if you would trust that role with the server itself.
Limits
Up to 100 snippets, each up to 100,000 characters of code and a label of up to 191 characters.
For developers
- The snippets are one option,
header_footer_snippetsinwp_options: a JSON list of{id, label, code, place, category, active, google_consent_mode}in page order.placeis a kitloom/site place (head,body_open,footer),categoryone offunctional,preferences,statistics,marketing.HeaderFooterOptionsdeclares it: the writer checks the rows for any writer and asks a user formanage_header_footer. Write throughSnippets::save($rows, $actor), which gives new rows an id. - An option, not a table: a save clears the page cache the way every option save does, and the snippets travel with the options in an export.
SnippetScriptsis thePageScriptsProvider: the active, non-empty snippets of a place, in order, on pages the site renders. Each goes throughSnippetConsent::gate(), which hands it to kitloom/site'sScriptConsent::gate()with its category andgoogle_consent_mode; a consent module answers, otherwise the code goes out as it is.php artisan vendor:publish --tag=kitloom-header-footer-configforkitloom-header-footer.php:filament.registerandnova.registerturn the admin sections off.- Tests, from the application that installs the package:
php vendor/bin/phpunit --testsuite HeaderFooter.