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 samectxyou'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: falsefor 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._inspectand_jsonare safe to leave registered, but watch what you pipe into them on a production page.
Next steps
- Mark debug pages with
draft: trueso 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
- Navigator module - the full option shape.
- Globals -
_snapshotand_baseline.features. - Filters - the debug filters in full.