The Problem with Global Layouts
As your SvelteKit application grows, you'll inevitably reach a point where different sections need radically different layouts. Your marketing homepage shouldn't share the same sidebar navigation as your authenticated dashboard. Your admin panel needs a completely different header than your public blog. Without proper layout isolation, you end up with deeply nested conditionals and fragile template logic that becomes a maintenance nightmare.
Layout groups and resets give you a powerful way to organize your routing structure so that different sections of your application can have entirely separate layout hierarchies. This guide walks you through layout groups and resets: a hands-on guide for engineers who need to structure medium to large SvelteKit applications with confidence.
Understanding Layout Groups with (app) and (marketing)
Layout groups use parentheses in your directory structure to create logical groupings without affecting the URL. A route under (app) and a route under (marketing) can both live at the root level of your URL structure, but each uses its own layout chain.
Here's a practical scenario: you're building a SaaS platform with a public marketing site and an authenticated application dashboard. Your directory structure might look like this:
src/routes/
├── (marketing)/
│ ├── +layout.svelte
│ ├── +layout.server.ts
│ ├── +page.svelte // /
│ ├── pricing/
│ │ └── +page.svelte // /pricing
│ └── blog/
│ └── [slug]/
│ └── +page.svelte // /blog/[slug]
├── (app)/
│ ├── +layout.svelte
│ ├── +layout.server.ts
│ ├── dashboard/
│ │ └── +page.svelte // /dashboard
│ ├── settings/
│ │ └── +page.svelte // /settings
│ └── projects/
│ └── [id]/
│ └── +page.svelte // /projects/[id]
└── +layout.svelte // Root layout (shared by both groups)The marketing layout handles branding, footer newsletter signup, and unauthenticated user experience:
// src/routes/(marketing)/+layout.svelte
<script>
import Header from '$lib/components/marketing/Header.svelte';
import Footer from '$lib/components/marketing/Footer.svelte';
</script>
<div class="marketing-wrapper">
<Header />
<main class="prose prose-lg">
<slot />
</main>
<Footer />
</div>
<style>
.marketing-wrapper {
display: flex;
flex-direction: column;
min-height: 100vh;
}
main {
flex: 1;
}
</style>The app layout uses a completely different structure with sidebar navigation and protected content:
// src/routes/(app)/+layout.svelte
<script>
import Sidebar from '$lib/components/app/Sidebar.svelte';
import TopNav from '$lib/components/app/TopNav.svelte';
import { page } from '$app/stores';
</script>
<div class="app-wrapper">
<Sidebar >
<nav>
<a href="/dashboard" class:active={$page.url.pathname === '/dashboard'}>
Dashboard
</a>
<a href="/projects" class:active={$page.url.pathname === '/projects'}>
Projects
</a>
</nav>
</Sidebar>
<div class="main-content">
<TopNav />
<main>
<slot />
</main>
</div>
</div>
<style>
.app-wrapper {
display: grid;
grid-template-columns: 250px 1fr;
min-height: 100vh;
}
</style>Notice that routes under (marketing) and (app) don't share any layout code. The grouping ensures complete isolation between your public and authenticated experiences.
Using +layout@reset to Break Inheritance
Sometimes you need to break out of the layout hierarchy entirely. This is where the @ reset syntax becomes invaluable. The pattern +layout@name tells SvelteKit which layout to use as the parent, allowing you to skip intermediate layout files.
Consider a modal or full-screen onboarding flow within your authenticated app. You might have routes like:
src/routes/
├── (app)/
│ ├── +layout.svelte
│ ├── dashboard/
│ │ └── +page.svelte
│ └── onboarding/
│ ├── +layout@(app).svelte // Resets to (app) layout
│ ├── +page.svelte // /onboarding
│ ├── company-details/
│ │ └── +page.svelte // /onboarding/company-details
│ └── team-setup/
│ └── +page.svelte // /onboarding/team-setupThe onboarding layout can be full-screen without the sidebar, while still being part of the app group:
// src/routes/(app)/onboarding/+layout@(app).svelte
<script>
import ProgressBar from '$lib/components/ProgressBar.svelte';
import { page } from '$app/stores';
</script>
<div class="onboarding-container">
<ProgressBar
steps={['Company', 'Team', 'Workspace']}
currentStep={getCurrentStep($page.url.pathname)}
/>
<div class="content">
<slot />
</div>
</div>
<style>
.onboarding-container {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
min-height: 100vh;
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
}
.content {
width: 100%;
max-width: 600px;
padding: 2rem;
background: white;
border-radius: 8px;
}
</style>The onboarding pages skip the regular app layout (with sidebar and top nav) and go straight to the (app) group parent, enabling full-screen presentation while keeping user context.
Separate Auth Layouts for Different User States
Real-world applications need entirely different layouts for unauthenticated versus authenticated states. You might create an (auth) layout group for login, signup, and password reset flows:
src/routes/
├── (auth)/
│ ├── +layout.svelte
│ ├── login/
│ │ └── +page.svelte // /login
│ ├── signup/
│ │ └── +page.svelte // /signup
│ └── reset-password/
│ └── +page.svelte // /reset-password
├── (marketing)/
│ └── ...
└── (app)/
└── ...The auth layout centers a form and keeps branding minimal:
// src/routes/(auth)/+layout.svelte
<script>
import Logo from '$lib/components/Logo.svelte';
</script>
<div class="auth-page">
<div class="auth-container">
<div class="auth-header">
<Logo size="md" />
</div>
<div class="auth-form">
<slot />
</div>
<div class="auth-footer">
<p>By signing in, you agree to our Terms of Service</p>
</div>
</div>
</div>
<style>
.auth-page {
display: flex;
align-items: center;
justify-content: center;
min-height: 100vh;
background: #f5f5f5;
}
.auth-container {
width: 100%;
max-width: 400px;
padding: 2rem;
background: white;
border-radius: 12px;
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
}
</style>You can add server-side logic to prevent authenticated users from accessing these routes:
// src/routes/(auth)/+layout.server.ts
import { redirect } from '@sveltejs/kit';
export function load({ locals }) {
if (locals.user) {
throw redirect(302, '/dashboard');
}
}Sharing Root Layout Data Across Groups
Despite layout isolation, you often need shared data available everywhere. The root +layout.server.ts executes for every route and can provide global context:
// src/routes/+layout.server.ts
import { getUser } from '$lib/server/auth';
import { getSettings } from '$lib/server/db';
export async function load({ locals, cookies }) {
const user = await getUser(locals.sessionId);
const settings = await getSettings();
return {
user,
settings: {
siteTitle: settings.name,
supportEmail: settings.supportEmail,
maintenanceMode: settings.maintenanceMode
}
};
}
Every layout and page has access to this data through the data prop:
// src/routes/(app)/dashboard/+page.svelte
<script>
export let data;
</script>
<h1>Welcome back, {data.user.name}!</h1>
Child layout groups can extend root data without losing parent-level context:
// src/routes/(app)/+layout.server.ts
import { getBillingInfo } from '$lib/server/billing';
export async function load({ locals, parent }) {
const parentData = await parent();
const billing = await getBillingInfo(locals.user.id);
return {
...parentData,
billing
};
}
Common Mistakes
Forgetting that reset points break hierarchy completely. When you use +layout@root.svelte, you lose access to all intermediate layout data. Always check what data your component needs before resetting.
Mixing logic between group layouts. Each layout group should be self-contained. Avoid checking for user roles or different states within a layout—use hooks in +layout.server.ts to redirect instead.
Not loading parent data properly. When extending a layout with export async function load({ parent }), always call await parent() first to preserve inherited data. Overwriting without spreading causes data loss.
Creating too many layout groups. Three groups (auth, marketing, app) cover most use cases. More than five usually indicates over-engineering. Evaluate whether you truly need layout separation or just conditional rendering.
Key Takeaways
- Layout groups with parentheses (
(name)) create isolated layout hierarchies for different sections of your app without affecting URLs - Use
+layout@namesyntax to reset layout inheritance and skip intermediate layouts when needed - Common groups are
(auth)for login flows,(marketing)for public pages, and(app)for authenticated user experiences - Root-level
+layout.server.tsprovides shared data to all groups; child layouts access it viaawait parent() - Keep layout logic simple—use server-side redirects to enforce access control rather than complex conditional rendering
- Plan your layout structure early; refactoring groups across a large application is friction-heavy






Comments
Loading comments…