Add Google Tag Manager to Next.js App Router

To set up GTM with Next.js App router you need a few things:

  1. GTM account:
  2. Container ID, e.g. GTM-AB2CDEF
  3. @next/third-parties package from Next.js

After you've created your Google Tag Manager account and acquired a Container ID, install the @next/third-parties library:

To load GTM for all routes in your Next.js application, include the <GoogleTagManager /> component in your root layout, making sure to pass the required Container ID.

import { GoogleTagManager } from '@next/third-parties/google'

export default function RootLayout({
}: Readonly<{
  children: React.ReactNode;
}>) {
  return (
    <html lang="en">
      {process.env.NODE_ENV === "production" ? (
        <GoogleTagManager gtmId="GTM-MJ2XRWJ" />
      ) : null}

This component only initialises the scripts that GTM provide, you have to implement tracking page_view yourself, if you're ready to track page views feel free to jump ahead, or keep reading to learn about how to track events such as clicks.

Track events

To track interactions such as clicks, use the sendGTMEvent function. It automatically pushes events to the dataLayer object. You won't be able to call sendGTMEvent if you don't include <GoogleTagManager /> in a parent layout, page or component.

'use client'

import { sendGTMEvent } from '@next/third-parties/google'

export function EventButton() {
  return (
        onClick={() => sendGTMEvent({ event: 'buttonClicked', value: 'xyz' })}
        Send Event

Interacting with dataLayer requires the use client directive.

Track page views

The previous steps only initialize the GTM script and enable tracking interactions such as button clicks. To start tracking page views, you have to do one of the following (don't mix or you'll have duplication):

Create a trigger

Read how to create triggers in my previous post called How to Integrate Google Tag Manager with Next.js.

Use a function

For this example I created a function called gtmPageView that can be used anywhere in the application. I only post an additional page_title prop, but this setup is capable of being extended to provide a lot more, such as ecommerce.

The page_view event is fired when either a route changes i.e. when a page is navigated to via <Link />, or when a page loads for the first time.

export const gtmPageView = (props: { [key: string]: any }) => {
  return window.dataLayer?.push({
    event: "page_view",
    url: window.location.href,
"use client";

import Template from "@/app/Template";
import { useEffect } from "react";
import { gtmPageView } from "@/lib/gtm";

export default function Page({ params }) {
  const slug = params.slug;

  useEffect(() => {
    if (slug) {
      const props = {
        page_title: slug,
  }, [slug]);

  return <Template title={slug} />;

Get the full code

The full code is available here, it's completely free.

Video walkthrough

In the video I connect to a container through Tag Assistant, fire events into window.dataLayer and show how the events can be viewed.

Walk through video of how I set this up

If you have any suggestions as to how the examples can be made clearer, easier to understand, or how I can add any missing use-cases you feel would help others, I'd love nothing more than to hear from you! Reach out to me on Twitter, or shoot me an email.

Written by Morgan Feeney

I’ve been designing and developing for almost 2 decades.
Read about me, and my work.

For juicy tips and exclusive content, subscribe to my FREE newsletter.