Google Tag Manager - v1.0.0

Snippet Installer

The snippet installer emits the standard Google Tag Manager <script> head snippet and <noscript> body fallback for a configured container ID. It's completely standalone — no OAuth, no base-package requirement — and safe to include unconditionally in shared layouts.

Blade directives

Set your container ID (see Environment variables) and drop the directives into your layout.

@gtmSnippet

Emits both the head <script> block and the body <noscript> fallback in one call. Simplest, but slightly less optimal than splitting the two blocks.

<!DOCTYPE html>
<html>
<head>
    @gtmSnippet
</head>

@gtmSnippetHead and @gtmSnippetBody

Splits the head and body blocks — Google's recommended placement for higher tag fidelity.

<!DOCTYPE html>
<html>
<head>
    @gtmSnippetHead
    {{-- other head content --}}
</head>
<body>
    @gtmSnippetBody
    {{-- page content --}}
</body>
</html>

Safe to include unconditionally

When the snippet is disabled or the container ID is missing, all three directives render an empty string — not a broken <script> tag or a <!-- disabled --> marker. That means:

  • You do not need @if( config('google-tag-manager.snippet.enabled') ) wrappers.
  • Local development without GTM_CONTAINER_ID set is silent — no console warnings.
  • Feature flags that toggle snippet.enabled at runtime take effect on the next request.

React component

For SPAs, mount <GtmSnippet /> once high in the tree:

import { GtmSnippet } from '@/js/vendor/google-tag-manager/react'

export function App() {
    return (
        <>
            <GtmSnippet containerId="GTM-XXXXXXX" />
            {/* ... */}
        </>
    )
}

Props:

Prop Type Default Purpose
containerId string Required. The GTM container ID.
dataLayerName string 'dataLayer' The dataLayer variable name. Only override when your app already uses a custom name.

Multiple mounts with the same containerId are safe — the installer keys the injected script by container ID and no-ops on duplicate calls.

The React component does not render the <noscript> body fallback. Server-render that block via @gtmSnippetBody, or add your own <noscript> where you mount the SPA.

Vue component

<script setup>
import { GtmSnippet } from '@/js/vendor/google-tag-manager/vue'
</script>

<template>
    <GtmSnippet container-id="GTM-XXXXXXX" />
    <!-- ... -->
</template>

Props mirror the React component:

Prop Type Default
containerId string — (required)
dataLayerName string 'dataLayer'

Same caveat about <noscript> — Vue-side is client-only.

How the container ID gets to the client

The Blade directives resolve google-tag-manager.snippet.container_id server-side and emit it inline in the <script> tag — no extra config or JS bridge needed.

For React and Vue, you pass the container ID as a prop. If your admin panel manages the container ID dynamically, expose it via your app's data layer or an API endpoint and thread it into the component prop.

Security notes

  • Container ID escaping. The Blade directive escapes the container ID for both the JavaScript string interpolation and the <noscript> src attribute. An injection payload in the container ID (e.g. GTM-X'); alert(1);//) will not break out of the JS string or the HTML attribute.
  • CSP. The GTM <script> block loads https://www.googletagmanager.com/gtm.js. If you use Content-Security-Policy, add https://www.googletagmanager.com to script-src and (for the noscript iframe) frame-src.
  • Consent Mode. The snippet does not implement Google Consent Mode. Handle consent in the container itself via GTM's built-in consent settings, or in your host app before rendering the snippet.
  • Components — the admin-side Livewire, React, and Vue components (not the snippet)
  • Configuration — every knob on the snippet.* config