02 - Project Setup & Structure
📋 Jump to TakeawaysCreating a Project
npx sv create my-app
cd my-app
npm install
npm run devnpm run dev starts a local server at http://localhost:5173. Changes auto-reload.
Project Structure
After creating a project, you'll see:
my-app/
├── src/
│ ├── routes/ ← Your pages live here
│ │ ├── +page.svelte ← Home page (/)
│ │ └── +layout.svelte ← Root layout (wraps all pages)
│ ├── lib/ ← Your reusable code
│ │ ├── components/ ← Shared components
│ │ └── server/ ← Server-only code (DB, secrets) — PROTECTED
│ ├── app.html ← HTML shell (you rarely edit this)
│ ├── hooks.server.ts ← Server hooks (auth, logging)
│ └── hooks.client.ts ← Client hooks (optional — only for global error handling)
├── static/ ← Static files (favicon, images)
├── svelte.config.js ← SvelteKit config
├── vite.config.ts ← Vite bundler config
├── tsconfig.json ← TypeScript config
└── package.jsonThe Two Most Important Folders
Your Pages - src/routes/
This is where you spend most of your time. Each folder = a URL:
src/routes/
├── +page.svelte → / (home page)
├── +layout.svelte → wraps all pages (nav, footer)
├── +layout.server.ts → loads data for the layout (server-only, e.g. auth user)
├── about/
│ └── +page.svelte → /about
├── blog/
│ └── +page.svelte → /blog
└── contact/
└── +page.svelte → /contactYour Reusable Code - src/lib/
Components, utilities, types, stores — anything shared across pages. Import using the $lib alias:
<!-- Instead of: import Card from '../../../lib/components/Card.svelte' -->
<script>
import Card from '$lib/components/Card.svelte';
</script>$lib always points to src/lib/ no matter where you import from.
$lib is one of several built-in aliases SvelteKit provides. Here's the full list:
| Alias | What it is | Example |
|---|---|---|
$lib |
Your shared code (src/lib/) |
import Card from '$lib/components/Card.svelte' |
$lib/server |
Server-only code (never sent to browser) | import { db } from '$lib/server/database' |
$app/navigation |
Client-side navigation | goto, invalidate, invalidateAll |
$app/forms |
Form enhancement | enhance |
$app/state |
Page state (URL, params, errors) | page |
$app/environment |
Runtime info | browser, dev |
$env/static/private |
Build-time secrets (server only) | DATABASE_URL, API_KEY |
$env/static/public |
Build-time public vars | PUBLIC_SITE_URL |
$env/dynamic/private |
Runtime secrets (server only) | Same as static but read at request time |
$env/dynamic/public |
Runtime public vars | Same as static but read at request time |
You don't need to memorize these. We'll use them throughout the course. Just know they exist and come back to this table when you need one.
🎯 Note: only $lib maps to a real folder (src/lib/). The $app and $env aliases are virtual modules provided by SvelteKit at build time, you won't find them in your file system.
Protected Server Code - src/lib/server/
src/lib/server/ is a special folder — SvelteKit prevents it from being imported in client-side code. If you try, the build fails. This is where you put database connections, API keys, and any code that must never reach the browser.
Only server-only files can import from it:
+page.server.ts,+layout.server.ts,+server.ts,hooks.server.ts✅+page.svelte,+page.ts❌ (build error)
The same protection applies to any file with .server in the name. A +page.server.ts is never sent to the browser. It runs on the server, returns data, and SvelteKit passes that data to +page.svelte as serialized JSON. The browser gets the data, not the code.
The rule: if it has server in the name (file or folder), it stays on the server.
Special Files in a Route
Each route folder can have these files (all optional except +page.svelte):
| File | What it does | Runs on |
|---|---|---|
+page.svelte |
The page UI | Browser |
+page.ts |
Loads data for the page | Server + Browser |
+page.server.ts |
Loads data (server only) + form actions | Server only |
+layout.svelte |
Wraps child pages with shared UI | Browser |
+layout.ts |
Loads data for the layout | Server + Browser |
+layout.server.ts |
Loads data for layout (server only) | Server only |
+error.svelte |
Error page | Browser |
+server.ts |
API endpoint (no UI) | Server only |
Don't memorize this — you'll learn each one in upcoming lessons. For now, just know:
.sveltefiles = UI (what the user sees).tsfiles = logic (data loading, form handling, API endpoints)serverin the name = runs only on the server (safe for secrets/DB)
Your First Page
Create src/routes/+page.svelte:
<!-- src/routes/+page.svelte -->
<h1>Hello SvelteKit!</h1>
<p>This is the home page.</p>That's it. Visit http://localhost:5173 and you'll see it. No imports, no configuration.
Adding a Second Page
Create src/routes/about/+page.svelte:
<!-- src/routes/about/+page.svelte -->
<h1>About</h1>
<p>This is the about page.</p>
<a href="/">← Back home</a>Visit http://localhost:5173/about. Navigation between pages is instant — SvelteKit handles it client-side.
The app.html Shell
src/app.html is the HTML template that wraps everything:
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
%sveltekit.head%
</head>
<body>
%sveltekit.body%
</body>
</html>%sveltekit.head%— SvelteKit injects CSS, meta tags, etc.%sveltekit.body%— SvelteKit injects your rendered page
You rarely need to edit this file.
Next Lesson
Now that you have a project, let's learn how routing works in detail — dynamic routes, parameters, and navigation.
Key Takeaways
src/routes/= pages. Folder structure = URL structuresrc/lib/= shared code. Import with$lib/+page.svelte= what the user sees+page.server.ts= server logic (data, forms)npm run dev= start developing