Table of Contents

Debugging with the navigator

When something looks off in a build, Baseline's debug helpers let you see the data Eleventy is working with from inside any template. By the end you'll know which helper to reach for, when, and how to keep them out of production.

The cost of leaving them in dev is essentially nothing; the cost of shipping them is real.

The navigator is Baseline's built-in inspection surface, and it comes in three flavours. The debug filters (_inspect, _json, _keys) and the runtime helpers (_runtime(), _ctx()) are always available, in every environment, the difference between them being that the helpers return live snapshots rather than formatting something you already hold. The navigator template is the one with a switch on it: a virtual page Baseline registers at /navigator-core.html, on in dev by default and off in production.

A snapshot, in this context, is a structured dump of state at the moment it's read.


What you will build

  • A debug page that prints selected data through Baseline's filters.
  • An inline section using _runtime(), _ctx(), and _snapshot.
  • Optionally, the navigator template enabled for a one-page view of the cascade.

Prerequisites

At least one page to inspect.


Enable (or skip) the navigator template

The navigator template is on by default in development and off in production. If that suits you, skip ahead to the debug page; no configuration needed.

To control it explicitly, the navigator option takes either a boolean or an object:

import baseline, { config as baselineConfig } from '@apleasantview/eleventy-plugin-baseline';
import settings from './src/_data/settings.js';

/** @param {import("@11ty/eleventy").UserConfig} eleventyConfig */
export default async function (eleventyConfig) {
	await eleventyConfig.addPlugin(
		baseline(settings, {
			navigator: {
				template: true,
				inspectorDepth: 2
			}
		})
	);
}

export const config = baselineConfig;

inspectorDepth is the depth the navigator page inspects to, here set to 2. It belongs to that page alone: the _inspect filter has its own default of 4 and does not read the option. Per-call options win in either place, so {{ page | _inspect({ depth: 4 }) }} is depth 4 wherever you write it.


Add a debug page

Create src/content/pages/debug-playground.md:

---
title: 'Debug Playground'
description: 'Inspect data with Baseline debug helpers.'
slug: 'debug-playground'
layout: 'layouts/base.njk'
---

## Collections (first 3)

<pre>
{% for item in collections.all.slice(0, 3) %}
- {{ item.url }}
{% endfor %}
</pre>

## Page keys

<pre>{{ _pageContext | _keys }}</pre>

## Page data (inspect)

<pre>{{ _pageContext | _inspect({ getters: true, depth: 2 }) }}</pre>

## Page data (json)

<pre>{{ _pageContext | _json(2) }}</pre>

Use the runtime and context helpers inline

Both helpers return live state at render time:

  • _runtime() returns { env, ctx, globals }: environment data, the current render context, and registered globals.
  • _ctx() returns just the current render context (the same ctx you'd find inside _runtime()).
<h2>Runtime (env + ctx + globals)</h2>
<pre>{{ _runtime() | _inspect() }}</pre>

<h2>Render context only</h2>
<pre>{{ _ctx() | _inspect({ depth: 2 }) }}</pre>

These dumps can be large. Use them locally; remove or guard them before shipping.


Read the _snapshot global

_snapshot is a computed global with the shape { contentMap, pageContext, seoGraph }. The content map is the per-build inventory of pages; pageContext is the full normalised map of all your content and modules read from internally; seoGraph is the resolved SEO handle for every page, the same one the head emits from.

<h2>Content map</h2>
<pre>{{ _snapshot.contentMap | _json(2) }}</pre>

<h2>Page context</h2>
<pre>{{ _snapshot.pageContext | _json(2) }}</pre>

One quirk worth knowing: _snapshot.contentMap is null on the navigator template itself, because the content-map event hasn't fired yet at that point in the lifecycle. Read it from any ordinary page instead.


Run and inspect

npm start

Open /debug-playground/ to see the filters and global outputs.


Visit the navigator template (optional)

With navigator.template: true, Baseline registers a virtual page at /navigator-core.html. It renders the same kind of snapshot as _runtime(), but as its own page rather than embedded in one of yours. Keep it disabled in production.


Production considerations

  • Set navigator: false for production builds.
  • _runtime() and _ctx() expose the full render context, including data-layer values you don't want public. Remove them from any template you ship.
  • _inspect and _json are safe to leave registered, but watch what you pipe into them on a production page.

Next steps

  • Mark debug pages with draft: true so they stay out of production automatically. The sitemaps and drafts tutorial covers the rest of the draft story.
  • Narrow your inspection by slicing collections or selecting specific data keys before piping them into a filter. Bigger dumps aren't always more useful.

See also