sveltejs/kit · warning

Placing %sveltekit.body% directly inside <body> is not recom

Error message

Placing %sveltekit.body% directly inside <body> is not recommended, as your app may break for users who have certain browser extensions installed.

Consider wrapping it in an element:

<div style="display: contents">
  %sveltekit.body%
</div>

What it means

SvelteKit recommends mounting the app inside a wrapper element rather than directly on `<body>`, because browser extensions that inject DOM nodes into `<body>` can break hydration or the app layout. When DEV detects the hydration target is `document.body`, it warns and suggests wrapping `%sveltekit.body%` in a `display: contents` div.

Source

Thrown at packages/kit/src/runtime/client/client.js:496

 * @param {Map<string, Map<string, T>>} map
 * @returns {Generator<[string, T]>} every entry of the cache map, keyed by remote key
 */
function* cache_entries(map) {
	for (const [id, entries] of map) {
		for (const [payload, entry] of entries) {
			yield [create_remote_key(id, payload), entry];
		}
	}
}

/**
 * @param {import('./types.js').SvelteKitApp} _app
 * @param {HTMLElement} _target
 * @param {Parameters<typeof _hydrate>[1]} [data]
 */
async function _start(_app, _target, data) {
	if (DEV && _target === document.body) {
		console.warn(
			'Placing %sveltekit.body% directly inside <body> is not recommended, as your app may break for users who have certain browser extensions installed.\n\nConsider wrapping it in an element:\n\n<div style="display: contents">\n  %sveltekit.body%\n</div>'
		);
	}

	if (payload.data) {
		const { q = {}, p = {}, l = {}, f = {} } = payload.data;

		// store the whole nodes — error records seed the corresponding
		// resources in a failed state when they are created during hydration
		for (const k in q) query_responses[k] = q[k];
		for (const k in l) query_responses[k] = l[k];
		for (const k in f) query_responses[k] = f[k];
		for (const k in p) prerender_responses[k] = p[k];
	}

	// detect basic auth credentials in the current URL
	// https://github.com/sveltejs/kit/pull/11179
	// if so, refresh the page without credentials

View on GitHub (pinned to 03f1687fe6)

Solutions

  1. Edit `src/app.html` to wrap `%sveltekit.body%` in a container div with `style="display: contents"`
  2. Re-test the app in DEV to confirm the warning disappears

Example fix

<!-- before -->
<body>
  %sveltekit.body%
</body>
<!-- after -->
<body>
  <div style="display: contents">%sveltekit.body%</div>
</body>
Defensive patterns

Strategy: validation

Validate before calling

// check app.html at build time
import { readFileSync } from 'node:fs';
const html = readFileSync('src/app.html', 'utf8');
if (!/<div[^>]*display:\s*contents[^>]*>\s*%sveltekit\.body%/.test(html)) {
  throw new Error('%sveltekit.body% must be wrapped in an element');
}

Type guard

function isBodyTarget(el) { return el === document.body; }

Prevention

When it happens

Trigger: An `app.html` template containing `%sveltekit.body%` as a direct child of `<body>` (no wrapping element), viewed in DEV; `_start` in packages/kit/src/runtime/client/client.js checks `_target === document.body`.

Common situations: Projects migrated from old app templates; hand-written `src/app.html` copied from outdated examples or created from scratch.

Related errors


AI-assisted analysis of sveltejs/kit@03f1687fe6 (2026-09-02). Data as JSON: /api/errors/4b15b0379975911e. Report an issue: GitHub.