In this comprehensive guide, we’ll explore SvelteKit and Svelte Store, two powerful tools that make building web applications a breeze. Whether you’re new to Svelte or coming from other frameworks, this guide will help you understand the fundamentals and best practices.
Understanding SvelteKit
SvelteKit is more than just a framework - it’s a complete solution for building web applications. It provides:
- File-based routing
- Server-side rendering (SSR)
- Static site generation (SSG)
- API routes
- Development environment with hot module replacement
Setting Up Your First SvelteKit Project
Let’s start by creating a new SvelteKit project:
npx sv create my-svelte-app --template skeleton
cd my-svelte-app
npm install
npm run dev
The project structure will look like this:
my-svelte-app/
├── src/
│ ├── routes/
│ │ └── +page.svelte
│ ├── lib/
│ └── app.html
├── static/
└── svelte.config.js
Understanding the Svelte CLI
The Svelte CLI (sv
) is a powerful toolkit for creating and maintaining Svelte applications. Let’s explore its features in detail.
Basic Usage
The basic syntax for creating a new project is:
npx sv create [options] [path]
Project Templates
The --template
option lets you choose from different project templates:
# Minimal setup
npx sv create my-app --template minimal
# Demo app with word guessing game
npx sv create my-app --template demo
# Library setup with svelte-package
npx sv create my-app --template library
TypeScript Support
You can configure TypeScript support using the --types
option:
# Use TypeScript (.ts files)
npx sv create my-app --types ts
# Use JSDoc for type annotations
npx sv create my-app --types jsdoc
# Skip TypeScript (not recommended)
npx sv create my-app --no-types
Additional Options
Control the setup process with these flags:
# Skip the interactive add-ons prompt
npx sv create my-app --no-add-ons
# Skip dependency installation
npx sv create my-app --no-install
A typical workflow might look like this:
# Create a TypeScript project with the demo template
npx sv create my-project \
--template demo \
--types ts \
--no-add-ons
Deep Dive into Svelte Store
State management is crucial in modern web applications. Svelte Store provides three types of stores, each serving different purposes.
1. Writable Stores
Writable stores are the most flexible type. Here’s a complete example:
// src/lib/stores/counter.ts
import { writable } from 'svelte/store';
export interface CounterStore {
count: number;
lastUpdated: Date;
}
function createCounterStore() {
const { subscribe, set, update } = writable<CounterStore>({
count: 0,
lastUpdated: new Date()
});
return {
subscribe,
increment: () => update(store => ({
count: store.count + 1,
lastUpdated: new Date()
})),
reset: () => set({ count: 0, lastUpdated: new Date() })
};
}
export const counterStore = createCounterStore();
2. Readable Stores
Perfect for external data sources that can’t be modified by the application:
// src/lib/stores/time.ts
import { readable } from 'svelte/store';
export const time = readable(new Date(), function start(set) {
const interval = setInterval(() => {
set(new Date());
}, 1000);
return function stop() {
clearInterval(interval);
};
});
3. Derived Stores
Derived stores depend on one or more other stores:
// src/lib/stores/derived-example.ts
import { derived } from 'svelte/store';
import { counterStore } from './counter';
export const doubledCount = derived(
counterStore,
$counter => $counter.count * 2
);
Building a Real-World Example
Let’s create a todo list application that demonstrates these concepts:
// src/lib/stores/todos.ts
import { writable } from 'svelte/store';
export interface Todo {
id: string;
text: string;
completed: boolean;
}
function createTodoStore() {
const { subscribe, set, update } = writable<Todo[]>([]);
return {
subscribe,
add: (text: string) => update(todos => [
...todos,
{ id: crypto.randomUUID(), text, completed: false }
]),
toggle: (id: string) => update(todos =>
todos.map(todo =>
todo.id === id ? { ...todo, completed: !todo.completed } : todo
)
),
remove: (id: string) => update(todos =>
todos.filter(todo => todo.id !== id)
)
};
}
export const todos = createTodoStore();
And here’s how to use it in a component:
<!-- src/routes/todos/+page.svelte -->
<script lang="ts">
import { todos } from '$lib/stores/todos';
import type { Todo } from '$lib/stores/todos';
let newTodoText = '';
function handleSubmit() {
if (newTodoText.trim()) {
todos.add(newTodoText);
newTodoText = '';
}
}
</script>
<form on:submit|preventDefault={handleSubmit}>
<input
bind:value={newTodoText}
placeholder="What needs to be done?"
/>
<button type="submit">Add Todo</button>
</form>
<ul>
{#each $todos as todo (todo.id)}
<li class:completed={todo.completed}>
<input
type="checkbox"
checked={todo.completed}
on:change={() => todos.toggle(todo.id)}
/>
<span>{todo.text}</span>
<button on:click={() => todos.remove(todo.id)}>Delete</button>
</li>
{/each}
</ul>
<style>
.completed {
text-decoration: line-through;
opacity: 0.6;
}
</style>
Best Practices and Tips
Store Organization
- Keep stores in a dedicated directory (e.g.,
src/lib/stores
) - Create separate files for different stores
- Use TypeScript for better type safety
- Keep stores in a dedicated directory (e.g.,
Performance Optimization
- Use derived stores instead of computing values in components
- Unsubscribe from stores when components are destroyed
- Use
$:
reactive statements wisely
Error Handling
- Implement error states in your stores
- Use try-catch blocks for async operations
- Provide meaningful error messages
Conclusion
SvelteKit and Svelte Store provide a powerful and intuitive way to build modern web applications. By following these patterns and best practices, you can create maintainable and scalable applications. Remember to:
- Start with simple stores and gradually add complexity
- Use TypeScript for better type safety
- Follow the Single Responsibility Principle
- Test your stores thoroughly
The examples provided here are just the beginning. As you build more complex applications, you’ll discover more ways to leverage these tools effectively.