Project Structure

SvelteForge Admin follows SvelteKit's file-based routing conventions and Svelte 5's component architecture. Every directory and file has a specific purpose — understanding this structure is key to extending the dashboard efficiently.

Because SvelteKit uses the filesystem as its router, the src/routes/ directory directly maps to URL paths. Route groups (directories in parentheses) share layouts without affecting the URL, which is how SvelteForge separates protected, auth, public, and documentation pages.

Complete File Tree

svelteforge-admin/
├── src/
│   ├── routes/                      # SvelteKit file-based routing
│   │   ├── (app)/                   # Protected routes (auth required)
│   │   │   ├── +layout.server.ts    # Auth guard + maintenance mode check
│   │   │   ├── +layout.svelte       # App shell (sidebar + topbar)
│   │   │   ├── +page.svelte         # Dashboard (/)
│   │   │   ├── users/               # User management CRUD
│   │   │   ├── content/             # CMS pages
│   │   │   │   ├── +page.svelte     # Content list
│   │   │   │   ├── new/             # Create new page
│   │   │   │   └── [id]/edit/       # Edit existing page (dynamic route)
│   │   │   ├── analytics/           # Charts and data visualization
│   │   │   ├── notifications/       # Notification center
│   │   │   ├── roles/               # Role management
│   │   │   ├── database/            # Database browser
│   │   │   └── settings/            # Application settings
│   │   ├── (auth)/                  # Public auth routes (no auth required)
│   │   │   ├── +layout.svelte       # Auth layout (centered card)
│   │   │   ├── login/               # Login page
│   │   │   │   ├── +page.svelte     # Login form
│   │   │   │   ├── +page.server.ts  # Login action (Argon2 verify)
│   │   │   │   ├── google/          # Google OAuth initiation
│   │   │   │   │   ├── +server.ts   # Redirect to Google
│   │   │   │   │   └── callback/    # Google OAuth callback
│   │   │   │   └── github/          # GitHub OAuth initiation
│   │   │   │       ├── +server.ts   # Redirect to GitHub
│   │   │   │       └── callback/    # GitHub OAuth callback
│   │   │   ├── register/            # Registration page
│   │   │   ├── forgot-password/     # Password reset request
│   │   │   ├── reset-password/      # Password reset form
│   │   │   └── lock/                # Screen lock (re-enter password)
│   │   ├── (public)/                # Public marketing pages
│   │   │   └── pricing/             # Pricing page
│   │   ├── docs/                    # Documentation (this site)
│   │   │   ├── +layout.svelte       # Docs layout (sidebar nav + prose)
│   │   │   ├── +page.svelte         # Introduction
│   │   │   ├── getting-started/     # Installation guide
│   │   │   ├── project-structure/   # This page
│   │   │   ├── authentication/      # Auth documentation
│   │   │   └── ...                  # Additional doc pages
│   │   ├── logout/                  # Logout action (server-only)
│   │   ├── api/
│   │   │   └── search/              # Command palette search endpoint
│   │   └── sitemap.xml/             # Auto-generated sitemap
│   │
│   ├── lib/
│   │   ├── server/                  # Server-only code (never sent to client)
│   │   │   ├── auth.ts              # Session management functions
│   │   │   ├── oauth.ts             # Arctic OAuth provider setup
│   │   │   ├── id.ts                # Cryptographic ID generator
│   │   │   └── db/
│   │   │       ├── index.ts         # Database connection (better-sqlite3)
│   │   │       ├── schema.ts        # Drizzle ORM schema definitions
│   │   │       └── seed.ts          # Database seeder script
│   │   │
│   │   ├── components/              # App-level Svelte 5 components
│   │   │   ├── app-sidebar.svelte   # Main navigation sidebar
│   │   │   ├── command-palette.svelte  # Ctrl+K search overlay
│   │   │   ├── notification-bell.svelte  # Notification dropdown
│   │   │   ├── theme-toggle.svelte  # Dark/light mode switch
│   │   │   ├── animated-counter.svelte  # Number animation component
│   │   │   ├── data-table-pagination.svelte  # Table pagination
│   │   │   ├── delete-confirm-dialog.svelte  # Confirmation modal
│   │   │   ├── role-change-dialog.svelte  # Role assignment modal
│   │   │   ├── user-form-dialog.svelte  # User create/edit form
│   │   │   └── apps-menu.svelte     # Application switcher menu
│   │   │
│   │   ├── components/ui/           # shadcn-svelte primitives
│   │   │   ├── button/
│   │   │   ├── card/
│   │   │   ├── dialog/
│   │   │   ├── dropdown-menu/
│   │   │   ├── input/
│   │   │   ├── table/
│   │   │   └── ...                  # More shadcn-svelte components
│   │   │
│   │   ├── hooks/                   # Svelte 5 reactive utilities
│   │   │   └── is-mobile.svelte.ts  # Responsive breakpoint detection
│   │   │
│   │   ├── utils/                   # Utility modules
│   │   │   ├── export.ts            # CSV and JSON export functions
│   │   │   └── user-agent.ts        # User-agent string parser
│   │   │
│   │   └── utils.ts                 # cn() helper (clsx + tailwind-merge)
│   │
│   ├── app.css                      # Tailwind CSS 4 theme configuration
│   ├── app.d.ts                     # TypeScript type definitions
│   ├── app.html                     # HTML shell template
│   └── hooks.server.ts              # SvelteKit server hooks
│
├── drizzle/                         # Generated Drizzle migration files
├── static/                          # Static assets (favicon, images)
├── tests/                           # Playwright E2E test files
│
├── svelteforge.db                   # SQLite database (gitignored)
├── drizzle.config.ts                # Drizzle ORM configuration
├── vite.config.ts                   # Vite + SvelteKit configuration
├── tailwind.config.ts               # Tailwind CSS configuration
├── tsconfig.json                    # TypeScript configuration
├── package.json                     # Dependencies and scripts
├── pnpm-lock.yaml                   # pnpm lockfile
└── .env.example                     # Environment variable template

Route Groups Explained

SvelteKit route groups are a powerful organizational tool. Directories wrapped in parentheses — like (app) — create layout boundaries without adding URL segments. SvelteForge uses four route groups to cleanly separate concerns:

(app)/ — Protected Routes

Every route inside (app)/ requires authentication. The auth guard lives in (app)/+layout.server.ts and runs before any page load:

// src/routes/(app)/+layout.server.ts
export const load: LayoutServerLoad = async ({ locals }) => {
  if (!locals.user) {
    redirect(302, "/login");
  }

  // Check maintenance mode
  const maintenanceSetting = await db.query.appSettings.findFirst({
    where: eq(appSettings.key, "maintenanceMode"),
  });
  if (maintenanceSetting?.value === "true" && locals.user.role !== "admin") {
    error(503, "The application is currently under maintenance.");
  }

  return { user: locals.user, ... };
};

This single SvelteKit layout server load function protects the dashboard, user management, content, analytics, notifications, roles, database, and settings pages — all without repeating auth logic in each route. The layout also enforces maintenance mode, blocking non-admin users with a 503 error.

The (app)/+layout.svelte provides the app shell: a collapsible sidebar, topbar with breadcrumbs, command palette, and notification bell.

(auth)/ — Public Auth Routes

Authentication pages that must be accessible without being logged in: login, register, forgot password, reset password, and screen lock. These use a minimal centered-card layout — no sidebar, no topbar.

OAuth callback routes nest under login/ as login/google/callback/ and login/github/callback/, keeping OAuth flow URLs clean and organized within SvelteKit's file-based router.

(public)/ — Marketing Pages

Public-facing pages like pricing that use their own layout — no auth required, no app shell. These pages are designed for unauthenticated visitors.

docs/ — Documentation

The documentation section (the pages you are reading now) uses its own layout with sidebar navigation and prose styling. This route group is not wrapped in parentheses because docs appears in the URL path.

Server-Only Code: src/lib/server/

SvelteKit enforces a strict server boundary. Any module inside #lib/server/ is guaranteed to never be bundled into client-side JavaScript. If you accidentally import a server module from a .svelte component, the build will fail with a clear error. This is critical for SvelteForge because sensitive code lives here:

auth.ts — Session Management

The core authentication module. Generates session tokens, hashes them with SHA-256, creates and validates sessions, and manages httpOnly cookies. Uses node:crypto for cryptographic operations — no external auth framework needed. See the Authentication page for a deep dive.

oauth.ts — Arctic OAuth Providers

Configures Google and GitHub OAuth via the Arctic library. Providers are environment-driven — they are null when env vars are missing, and the login page conditionally renders social login buttons. This means OAuth is entirely optional; the dashboard works with password-only auth out of the box.

id.ts — Cryptographic ID Generator

A simple utility that generates cryptographically random IDs using crypto.getRandomValues() and base32 encoding. Used for user IDs, OAuth account IDs, and password reset tokens throughout the application.

db/ — Database Layer

  • index.ts — Creates the better-sqlite3 connection with WAL mode enabled for concurrent reads. Exports the Drizzle ORM db instance.
  • schema.ts — Defines all database tables using Drizzle's type-safe schema builder: users, sessions, pages, notifications, oauthAccounts, appSettings, and passwordResetTokens. Each table export includes inferred TypeScript types.
  • seed.ts — Populates the database with sample data. Runs via pnpm db:seed using tsx (not SvelteKit's runtime), so it uses relative imports instead of #lib/ aliases.

App-Level Components: src/lib/components/

These are Svelte 5 components built with the runes API ($state, $props, $derived). Each component is self-contained with its own reactive state:

ComponentPurpose
app-sidebar.svelteMain navigation sidebar with collapsible groups, user avatar, and role display. Uses Svelte 5 $state for open/close tracking.
command-palette.svelteKeyboard-driven search overlay (Ctrl+K / Cmd+K). Queries the /api/search endpoint and renders results in real time.
notification-bell.svelteTopbar notification icon with unread count badge and dropdown list of recent notifications.
theme-toggle.svelteDark/light mode switch using mode-watcher. Accesses mode.current (runes object, not a Svelte store).
animated-counter.svelteSmoothly animates between numeric values for dashboard stat cards.
data-table-pagination.svelteReusable pagination controls for data tables with page size selection.
delete-confirm-dialog.svelteConfirmation modal for destructive actions (delete user, delete page, etc.).
role-change-dialog.svelteModal for changing a user's role (admin, editor, viewer) with confirmation.
user-form-dialog.svelteCreate/edit user form in a dialog, used by the user management page.
apps-menu.svelteApplication switcher dropdown in the topbar for navigating between app sections.

UI Primitives: src/lib/components/ui/

This directory contains shadcn-svelte components — accessible, composable UI primitives built on Bits UI. These are copy-pasted into your project (not installed as a dependency), giving you full control over their source.

Important: Do not edit these files directly. To update or add components, use the CLI:

npx shadcn-svelte@latest add button
npx shadcn-svelte@latest add dialog
npx shadcn-svelte@latest add dropdown-menu

shadcn-svelte components are fully compatible with Svelte 5 runes and use @render snippets instead of slots for child content.

Reactive Utilities: src/lib/hooks/

Svelte 5 runes enable reactive hooks similar to React's custom hooks, but with compile-time optimization. The .svelte.ts extension tells the Svelte compiler to process runes in these files:

is-mobile.svelte.ts

A reactive hook that tracks viewport width using $state and $effect. Returns a boolean that automatically updates when the window resizes. Used by the sidebar to determine default open/closed state on mobile.

Utility Modules: src/lib/utils/

FilePurpose
export.tsFunctions to export data tables as CSV or JSON files. Handles column mapping, date formatting, and triggers browser download.
user-agent.tsParses user-agent strings to extract browser name and OS. Used in the Settings page to display active sessions with device info.

src/lib/utils.ts

The cn() helper function, which combines clsx (conditional class names) with tailwind-merge (deduplication of Tailwind classes). Used extensively by shadcn-svelte components and throughout the application:

import { clsx, type ClassValue } from "clsx";
import { twMerge } from "tailwind-merge";

export function cn(...inputs: ClassValue[]) {
  return twMerge(clsx(inputs));
}

App-Level Configuration Files

src/app.css — Tailwind CSS 4 Theme

Tailwind CSS 4 replaces the traditional JavaScript config with native CSS. SvelteForge defines its design tokens using the @theme directive with OKLCH colors, supporting both light and dark modes. This is where you customize the color palette, border radii, fonts, and other design tokens.

src/app.d.ts — TypeScript Definitions

Extends SvelteKit's App.Locals interface to include user and session properties. This ensures TypeScript knows about the authenticated user data available in every server load function and form action:

declare global {
  namespace App {
    interface Locals {
      user: SessionUser | null;
      session: Session | null;
    }
  }
}

src/hooks.server.ts — Server Hooks

SvelteKit server hooks run on every single request. SvelteForge uses this to validate the session cookie, populate event.locals.user and event.locals.session, auto-extend session cookies, and update session metadata (user agent and IP address). This is the foundation of the authentication system — see Authentication for details.

Root Configuration Files

FilePurpose
vite.config.tsVite configuration with the SvelteKit plugin. Marks LayerChart and svelte-ux as noExternal for SSR compatibility. Configures Vitest.
drizzle.config.tsDrizzle ORM configuration pointing to src/lib/server/db/schema.ts as the schema source and svelteforge.db as the SQLite database file.
vite.config.tsSvelteKit configuration, Node adapter, Tailwind, and Vitest.
package.jsonProject dependencies and npm scripts. Key scripts: dev, build, check, db:push, db:seed, test.
tsconfig.jsonTypeScript configuration extending SvelteKit's recommended settings.
.env.exampleTemplate for environment variables: OAuth client IDs/secrets, ORIGIN, and database path.

Database File

The SQLite database file svelteforge.db lives at the project root. It is gitignored and created automatically when you run pnpm db:push. The database runs in WAL (Write-Ahead Logging) mode for better concurrent read performance. You will also see svelteforge.db-shm and svelteforge.db-wal files — these are WAL support files managed by SQLite.

To inspect the database visually, use pnpm db:studio to open Drizzle Studio in your browser.

Need More?

Go Premium with DashboardPack

SvelteForge Admin gives you a clean, well-organized Svelte 5 + SvelteKit foundation. When your project outgrows it — 50+ pages, multiple dashboard layouts, advanced CRUD generators, theme customizers, and production-grade components — DashboardPack has you covered.

  • Apex (Svelte) — SvelteKit admin with 6 dashboards and 39 demo pages
  • Zenith — Modern analytics dashboard with advanced data visualization
  • Signal — Real-time monitoring dashboard with live data feeds
  • Ember — Sleek SaaS dashboard with subscription management
  • Flux — Minimalist admin with focus on speed and simplicity