Building Modern Web Apps with Svelte: Complete Guide
What is Svelte?
Svelte is a radical new approach to building user interfaces. Unlike traditional frameworks such as React or Vue that do most of their work in the browser (runtime), Svelte shifts that work into a compile step that happens when you build your app. It converts your declarative components into highly efficient imperative JavaScript that surgically updates the DOM. This results in smaller bundle sizes, faster initial loads, and minimal runtime overhead.
Svelte uses a single-file component structure where HTML, CSS, and JavaScript coexist inside .svelte files. It provides reactive declarations, scoped CSS, and a simple mental model without the need for virtual DOM diffing. Svelte also includes built-in features like stores, animations, and transitions, making it a full-featured framework for building modern web applications.
Why Svelte Matters
Performance and simplicity are at the core of why developers are adopting Svelte. Here are key reasons it matters:
- No Virtual DOM: Svelte compiles to vanilla JavaScript that directly manipulates the DOM, eliminating the overhead of virtual DOM reconciliation.
- Small Bundle Size: The compiler produces minimal code – often just a few kilobytes for a complete app.
- True Reactivity: Simple assignments (
count = 0) trigger updates automatically, without hooks or setState. - Scoped Styles by Default: CSS written inside a component is automatically scoped to that component.
- Easier Learning Curve: Less boilerplate and a straightforward syntax make it accessible to beginners.
- Built-in State Management: Stores and context are first-class citizens, reducing the need for external libraries.
How to Use Svelte: Step-by-Step
Setting Up a Project
The fastest way to start is using the official scaffolding tool:
npx degit sveltejs/template my-svelte-app
cd my-svelte-app
npm install
npm run dev
This creates a basic Svelte project with a development server. Alternatively, you can use Vite (recommended for modern builds):
npm create vite@latest my-app -- --template svelte
cd my-app
npm install
npm run dev
Your First Component
Components live in .svelte files. Here is a simple counter component (Counter.svelte):
<script>
let count = 0;
function increment() {
count += 1;
}
function decrement() {
count -= 1;
}
</script>
<main>
<h1>Counter: {count}</h1>
<button on:click={decrement}>-</button>
<button on:click={increment}>+</button>
</main>
<style>
h1 {
color: #333;
}
button {
margin: 0 0.5rem;
padding: 0.5rem 1rem;
}
</style>
Reactive Declarations
Use $: to declare reactive values that automatically update when dependencies change:
<script>
let price = 10;
let quantity = 2;
$: total = price * quantity;
$: discount = total > 50 ? 0.1 : 0;
$: finalPrice = total - total * discount;
</script>
<p>Price: {price} | Quantity: {quantity}</p>
<p>Total: {total} | Discount: {discount * 100}% | Final: {finalPrice}</p>
Props (Passing Data to Children)
Use the export keyword to declare props:
<!-- Greeting.svelte -->
<script>
export let name = 'World';
</script>
<h2>Hello {name}!</h2>
<!-- App.svelte -->
<script>
import Greeting from './Greeting.svelte';
</script>
<Greeting name="Svelte" />
<Greeting /> <!-- uses default 'World' -->
Conditional Rendering and Loops
Svelte uses {#if}, {:else if}, {:else}, and {#each} blocks:
<script>
let items = ['Apple', 'Banana', 'Cherry'];
let showList = true;
</script>
<button on:click={() => showList = !showList}>
{showList ? 'Hide' : 'Show'} List
</button>
{#if showList}
<ul>
{#each items as item, i}
<li>{i + 1}: {item}</li>
{/each}
</ul>
{:else}
<p>List is hidden.</p>
{/if}
Event Handling and Two-Way Binding
Use on:event for DOM events and bind:value for input bindings:
<script>
let name = '';
let showAlert = false;
function handleSubmit() {
showAlert = true;
setTimeout(() => showAlert = false, 2000);
}
</script>
<input bind:value={name} placeholder="Enter your name" />
<button on:click={handleSubmit}>Submit</button>
{#if showAlert}
<p>Hello, {name}!</p>
{/if}
Stores for Shared State
Svelte provides writable, readable, and derived stores. Create a store file (store.js):
import { writable } from 'svelte/store';
export const count = writable(0);
Then use it in any component with the $ prefix for auto-subscription:
<script>
import { count } from './store.js';
function reset() {
count.set(0);
}
function add() {
count.update(n => n + 1);
}
</script>
<p>Count: {$count}</p>
<button on:click={add}>+1</button>
<button on:click={reset}>Reset</button>
Lifecycle Functions
Svelte offers onMount, onDestroy, beforeUpdate, and afterUpdate:
<script>
import { onMount, onDestroy } from 'svelte';
let timer;
onMount(() => {
timer = setInterval(() => {
console.log('tick');
}, 1000);
});
onDestroy(() => {
clearInterval(timer);
});
</script>
Animations and Transitions
Svelte includes built-in animation directives like transition:fade, transition:slide, and custom transitions:
<script>
import { fade, fly } from 'svelte/transition';
let visible = false;
</script>
<button on:click={() => visible = !visible}>
Toggle
</button>
{#if visible}
<p transition:fade>I fade in and out</p>
<p transition:fly={{ y: 200, duration: 500 }}>I fly in from below</p>
{/if}
Best Practices for Svelte Development
- Keep Components Small and Focused: Each component should do one thing well. Extract reusable pieces into their own files.
- Use Reactive Statements Sparingly:
$:is powerful but can become hard to trace if overused. Prefer functions for complex logic. - Leverage Stores for Cross-Component State: Avoid prop drilling by using stores or context. For simple shared state, a writable store is ideal.
- Organize Your Project by Feature: Group related components, stores, and utilities in folders (e.g.,
components/,stores/,lib/). - Use TypeScript for Larger Apps: Svelte supports TypeScript natively. Add
lang="ts"to your script tag and enjoy type safety. - Minimize Side Effects in Reactive Blocks: Avoid triggering API calls directly in
$:; useonMountor explicit function calls instead. - Utilize the
svelte:headComponent: Manage document head (title, meta tags) inside any component without a library:
<svelte:head>
<title>My Svelte App - Home</title>
<meta name="description" content="Welcome to my app" />
</svelte:head>
- Use
bind:thisfor Direct DOM Access: When you need to interact with a DOM element directly (e.g., canvas, video), bind it to a variable:
<script>
let canvasEl;
function draw() {
const ctx = canvasEl.getContext('2d');
// draw something
}
</script>
<canvas bind:this={canvasEl} width="400" height="300" />
- Lazy Load Components with Dynamic Imports: Use
import()inside anonMountor with<svelte:component>to split code:
<script>
import { onMount } from 'svelte';
let HeavyComponent;
onMount(async () => {
HeavyComponent = (await import('./HeavyComponent.svelte')).default;
});
</script>
{#if HeavyComponent}
<svelte:component this={HeavyComponent} />
{/if}
- Always Use Scoped CSS: By default, styles in a
<style>tag are scoped. To apply global styles, wrap them in:global(...). - Test Your Components: Use testing libraries like
@testing-library/svelteto write unit and integration tests. Svelte's compiled output makes tests reliable. - Optimize for Production: Use
npm run build(Vite) to generate optimized bundles. Enable code splitting and analyze bundle size with tools likerollup-plugin-visualizer.
Conclusion
Svelte offers a refreshingly simple yet powerful approach to building modern web applications. By shifting the heavy lifting from runtime to compile time, it delivers exceptional performance, small bundles, and a delightful developer experience. Its intuitive syntax, built-in reactivity, scoped CSS, and first-class stores make it an excellent choice for projects of any scale — from simple interactive widgets to full-fledged single-page applications. As you continue exploring Svelte, you'll discover a vibrant ecosystem, including SvelteKit for full-stack apps, Svelte Native for mobile, and a growing community of resources. Start building with Svelte today and experience the joy of writing less code while achieving more. The future of front-end development is compiled, and Svelte is leading the way.