Layout Switcher Troubleshooting

This page documents common issues encountered while building the layout switcher and their solutions.

Issue 1: Click Events Not Firing

Symptom: Clicking layout buttons in dev toolbar does nothing.

Root Cause: The astro-dev-toolbar-window custom element uses Shadow DOM internally. When setting innerHTML directly on it and then using querySelectorAll(), the elements weren't found because they were slotted into the shadow DOM.

Fix: Wrap content in a standard div element:

// Before (broken)
windowEl.innerHTML = html;
windowEl.querySelectorAll('.option-btn').forEach(btn => { ... });

// After (working)
const contentWrapper = document.createElement('div');
contentWrapper.innerHTML = html;
windowEl.appendChild(contentWrapper);
contentWrapper.querySelectorAll('.option-btn').forEach(btn => { ... });

File: src/dev-toolbar/layout-selector.ts

Issue 2: URL Changes But Layout Doesn't

Symptom: Clicking a layout button adds ?layout=doc_style2 to the URL, page reloads, but layout remains unchanged.

Root Cause: In Astro's static mode, Astro.url.searchParams doesn't include query parameters from the actual HTTP request. The URL is constructed from site config and path, not the request.

Debug Output:

Astro.url.href: http://localhost:4321/docs/page  // No query params!
Astro.url.search:
layoutOverride: null

Fix: Switch to server mode so middleware can intercept requests:

// astro.config.mjs
export default defineConfig({
  output: 'server',
  // ...
});

Issue 3: Hybrid Mode Removed in Astro 5

Symptom: Error on startup:

The output: "hybrid" option has been removed. Use output: "static" instead.

Root Cause: Astro 5.x removed the hybrid output mode.

Fix: Use output: 'server' instead. In Astro 5, static is the default and server is for on-demand rendering.

Issue 4: getStaticPaths() Ignored Warning

Symptom: Warning in console:

[WARN] getStaticPaths() ignored in dynamic page /src/pages/[...slug].astro

Root Cause: In server mode, getStaticPaths() doesn't run - pages render on-demand without pre-computed paths/props.

Fix: Add dynamic props loading that computes page data from the URL when Astro.props is empty:

let { pageType, doc, ... } = Astro.props;

// Server mode: props are empty, compute from URL
if (!pageType) {
  const slug = Astro.params.slug || '';
  // Match URL to config and load content...
}

Issue 5: LayoutComponent Undefined

Symptom: Error:

Unable to render LayoutComponent because it is undefined!

Root Cause: In server mode without dynamic props loading, configLayout was undefined because Astro.props was empty.

Fix: Same as Issue 4 - ensure all required props are computed dynamically when in server mode.

Debugging Checklist

1. Verify Server Mode

Check astro.config.mjs:

output: 'server',

2. Check Middleware Logs

Terminal should show:

[middleware] URL: http://...?layout=doc_style2 | layout: doc_style2

3. Verify Dev Toolbar Registration

Check that devToolbarIntegration() is in the integrations array:

integrations: [
  mdx(),
  devToolbarIntegration(),
],

4. Check Layout Exists

Layouts must exist at:

src/layouts/docs/styles/{layout_name}/Layout.astro
src/layouts/blogs/styles/{layout_name}/IndexLayout.astro
src/layouts/blogs/styles/{layout_name}/PostLayout.astro

5. Browser Console

Check for JavaScript errors that might prevent the dev toolbar from functioning.

Quick Reference: Files Involved

File Purpose
astro.config.mjs Set output: 'server'
src/middleware.ts Capture ?layout= param
src/env.d.ts TypeScript types for Astro.locals
src/pages/[...slug].astro Read layout override, dynamic props
src/dev-toolbar/integration.ts Register toolbar app
src/dev-toolbar/layout-selector.ts UI and click handlers