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 credentialsView on GitHub (pinned to 03f1687fe6)
Solutions
- Edit `src/app.html` to wrap `%sveltekit.body%` in a container div with `style="display: contents"`
- 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
- Keep the wrapper `<div style="display: contents">` around `%sveltekit.body%` in app.html
- Re-verify app.html after SvelteKit version upgrades
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
- \u001B[1m\u001B[31mRemoving comments in transformPageChunk c
- The properties of `form.fields` are virtual, so operators li
- The form action returned an error, but +error.svelte wasn't
- Avoid calling `fetch` eagerly during server-side rendering —
AI-assisted analysis of sveltejs/kit@03f1687fe6 (2026-09-02).
Data as JSON: /api/errors/4b15b0379975911e.
Report an issue: GitHub.