Introduction: Why Svelte Stands Apart
Svelte represents a fundamentally different approach to building user interfaces. While frameworks like React and Vue do the bulk of their work in the user's browser while the application runs, Svelte shifts that work into a compile step that happens only when you build your application.
The outcome of this compiler-first approach produces smaller application bundles and better runtime performance, but it also creates a more approachable developer experience for those new to modern frontend tooling. Svelte sticks closely to the classic web development model of HTML, CSS, and JavaScript, simply adding a few purposeful extensions. This results in fewer concepts and tools to learn compared to alternatives as explained in MDN's Svelte getting started guide.
Whether you're building a simple interactive widget or a complex single-page application, Svelte provides the primitives you need without the weight you don't. Its elegant reactivity system, introduced in Svelte 5 as "runes," makes managing state intuitive and efficient. The framework's minimalism doesn't mean limited power--it means carefully chosen features that work together harmoniously.
What You'll Learn in This Guide
This comprehensive guide walks you through building your first Svelte application from scratch. You'll discover how to set up your development environment, understand the component model, master reactivity with the new runes syntax, and deploy your creation to production. By the end, you'll have a solid foundation for building anything from simple interactive components to full-featured web applications using Svelte and SvelteKit.
Prerequisites and Environment Setup
Before diving into Svelte development, ensure your machine has the necessary foundations in place. Svelte is a compiler, which means you need a proper Node.js environment to transform your source code into optimized JavaScript bundles.
Required Software
Node.js and npm: The foundation of modern JavaScript development. Visit nodejs.org and download the Long-Term Support (LTS) version, which offers the best stability for production development. Node.js includes npm (the Node Package Manager) for managing dependencies and npx (the Node Package Runner) for executing commands without global installation. Verify your installation by opening a terminal and running node --version and npm --version--both commands should display version numbers without errors.
Terminal familiarity: You'll need basic comfort with command-line operations. Common commands include cd for changing directories, ls or dir for listing files, and basic file operations. If you're on Windows, consider using Windows Subsystem for Linux (WSL) or Git Bash for a more consistent experience with Unix-like commands that many Svelte tutorials assume.
Code editor: While any text editor works, Visual Studio Code offers excellent Svelte support through official extensions. The Svelte for VS Code extension provides syntax highlighting, IntelliSense, and error checking that significantly improves the development experience.
Creating Your First Svelte Project
The fastest way to scaffold a new Svelte project uses the sv command-line tool, which is the official way to create Svelte and SvelteKit applications. Open your terminal and execute the following commands to create a new project:
npx sv create my-svelte-app
cd my-svelte-app
npm install
npm run dev
The npx sv create command initiates an interactive setup process that asks several questions about your project preferences. You'll choose whether to create a SvelteKit application (recommended for most use cases) or a pure Svelte project, select your preferred language (JavaScript or TypeScript), and optionally add linting and formatting tools. For beginners, accepting the defaults is perfectly acceptable--they represent current best practices.
After running these commands, navigate to the displayed local URL (typically http://localhost:5173) to see your new application running. The development server includes hot module replacement, meaning changes you make to your source files automatically appear in the browser without requiring a full refresh. This tight feedback loop accelerates the learning process and makes experimentation frictionless.
Understanding the Project Structure
A newly created Svelte project contains several directories and files that serve specific purposes. The src directory holds your application source code, with src/routes containing pages when using SvelteKit. Static assets like images belong in the public or static folder, depending on your configuration. Configuration files like svelte.config.js and vite.config.js control how Svelte compiles your code and how Vite builds your application.
Everything you need to build modern web applications
Compiler-Based
No virtual DOM overhead--Svelte compiles your code to efficient vanilla JavaScript at build time.
Svelte 5 Runes
Explicit reactivity with $state, $derived, and $effect for predictable state management.
Scoped Styles
CSS defined in components stays in components--no more global namespace pollution.
SvelteKit Integration
Full application framework with file-based routing, SSR, and optimized delivery.
TypeScript Support
First-class TypeScript integration for type safety and better developer experience.
Transition Primitives
Built-in animation and transition system for polished user interfaces.
The Svelte Component Model
At the heart of Svelte development is the component--a self-contained piece of UI that combines HTML, JavaScript, and CSS. Svelte components live in .svelte files and follow a clean, intuitive structure that separates concerns without unnecessary complexity.
Anatomy of a Svelte Component
A Svelte component file typically contains up to three sections: a <script> block for logic, the template markup for structure, and a <style> block for component-specific CSS. Here's a simple example demonstrating each section:
<script>
let count = $state(0);
function increment() {
count += 1;
}
</script>
<button onclick={increment}>
Clicked {count} {count === 1 ? 'time' : 'times'}
</button>
<style>
button {
padding: 0.75rem 1.5rem;
font-size: 1rem;
background: #ff3e00;
color: white;
border: none;
border-radius: 4px;
cursor: pointer;
}
button:hover {
background: #e63800;
}
</style>
This component maintains internal state (count), responds to user interaction through an event handler (onclick), displays dynamic content using curly braces, and includes scoped styles that won't leak to other parts of your application.
Svelte 5 introduced a new reactivity system based on runes--special functions that explicitly declare reactive behavior. This replaces the older let and $: syntax with more explicit and intuitive alternatives that work consistently across all contexts, including outside components.
Reactivity with Svelte 5 Runes
Svelte 5 introduced runes for explicit reactivity, providing clearer mental models for when and why reactivity occurs, reducing bugs and making code easier to reason about.
$state - Reactive Variables
The core rune is $state, which declares a reactive piece of state. When the state changes, Svelte automatically updates any part of the DOM that references it. This works for simple values, objects, and arrays alike.
<script>
let name = $state('World');
</script>
<h1>Hello, {name}!</h1>
$derived - Computed Values
For derived values that compute from other state, use $derived. The derived value updates automatically whenever its dependencies change.
<script>
let items = $state([1, 2, 3, 4, 5]);
let total = $derived(items.reduce((a, b) => a + b, 0));
let doubled = $derived(items.map(n => n * 2));
</script>
<p>Total: {total}</p>
<p>Doubled: {doubled.join(', ')}</p>
$effect - Side Effects
The $effect rune runs code whenever the reactive values it reads change, making side effects like logging, syncing with external stores, or manipulating the DOM outside Svelte's purview straightforward:
<script>
let name = $state('World');
$effect(() => {
console.log(`Name changed to: ${name}`);
document.title = `Hello ${name}`;
});
</script>
Props: Passing Data Between Components
Components become truly powerful when they can accept input from their parents. Svelte's prop system uses $props() to declare expected inputs, making component interfaces explicit and self-documenting.
Declaring and Using Props
<script>
let { title, count = 0, onIncrement } = $props();
</script>
<div class="card">
<h2>{title}</h2>
<p>Count: {count}</p>
<button onclick={onIncrement}>Increment</button>
</div>
<style>
.card {
padding: 1.5rem;
border: 1px solid #e0e0e0;
border-radius: 8px;
}
</style>
When using this component, you pass props as HTML attributes:
<Card
title="My Counter"
count={currentCount}
onIncrement={() => currentCount += 1}
/>
Props flow down from parent to child, creating a clear data architecture. For components that need to modify values passed from parents, use callback functions (as onIncrement demonstrates) rather than attempting to mutate props directly, which would violate unidirectional data flow principles.
Default Values and Type Safety
Props can include default values directly in the destructuring pattern, providing sensible fallbacks when parents don't specify certain values. When using TypeScript, you can add type annotations for compile-time validation:
<script lang="ts">
interface CardProps {
title: string;
count?: number;
onIncrement?: () => void;
}
let { title, count = 0, onIncrement = () => {} }: CardProps = $props();
</script>
TypeScript integration catches prop mismatches during development, preventing runtime errors from reaching production. This type safety becomes increasingly valuable as applications grow in complexity.
Event Handling and User Interaction
Building interactive applications requires responding to user actions. Svelte provides clean syntax for handling DOM events and custom events emitted by components.
DOM Event Handlers
Attach event handlers using the on prefix followed by the event name. Svelte supports all standard DOM events including click, submit, input, keydown, focus, and many others.
<script>
let x = $state(0);
let y = $state(0);
function handleMouseMove(event) {
x = event.clientX;
y = event.clientY;
}
</script>
<div onmousemove={handleMouseMove}>
Mouse position: ({x}, {y})
</div>
<style>
div {
height: 200px;
background: #f5f5f5;
display: flex;
align-items: center;
justify-content: center;
cursor: crosshair;
}
</style>
Component Events with Callbacks
Components communicate upward through callback props. This pattern keeps components reusable and testable while maintaining clear data flow:
<Button onclick={() => alert('Clicked!')}>
Click Me
</Button>
The event object is automatically passed to handler functions when declared inline or explicitly when using named functions.
Conditionals and Loops: Controlling DOM Structure
Svelte's template syntax includes familiar control flow constructs that directly map to JavaScript logic, making it easy to express complex UI patterns.
Conditional Rendering with {#if}
Show or hide elements based on boolean state:
<script>
let visible = $state(true);
let count = $state(0);
</script>
<button onclick={() => visible = !visible}>
{visible ? 'Hide' : 'Show'} Counter
</button>
{#if visible}
<div class="counter">
<p>Count: {count}</p>
</div>
{:else}
<p>Counter is hidden</p>
{/if}
The {:else} and {:else if} branches handle alternative conditions cleanly:
{#if status === 'loading'}
<p>Loading...</p>
{:else if status === 'error'}
<p>Error loading data</p>
{:else}
<p>Data loaded successfully</p>
{/if}
List Rendering with {#each}
Render collections using keyed iteration. The (item.id) key expression is crucial--it tells Svelte how to track items across re-renders, ensuring efficient updates when the collection changes:
<script>
let items = $state([
{ id: 1, name: 'Apple', color: 'red' },
{ id: 2, name: 'Banana', color: 'yellow' },
{ id: 3, name: 'Cherry', color: 'darkred' }
]);
</script>
<ul>
{#each items as item (item.id)}
<li style="color: {item.color}">{item.name}</li>
{:else}
<li>No items in the list</li>
{/each}
</ul>
Building a Practical Example: Interactive Todo App
Let's apply these concepts by building a simple todo list application. This example demonstrates state management, props, events, and control flow working together.
TodoItem Component
<script>
let { todo, onToggle, onDelete } = $props();
</script>
<li class:completed={todo.completed}>
<label>
<input
type="checkbox"
checked={todo.completed}
onchange={() => onToggle(todo.id)}
/>
<span>{todo.text}</span>
</label>
<button onclick={() => onDelete(todo.id)}>×</button>
</li>
<style>
li {
display: flex;
align-items: center;
padding: 0.5rem;
border-bottom: 1px solid #eee;
}
li.completed span {
text-decoration: line-through;
opacity: 0.6;
}
label {
flex: 1;
display: flex;
align-items: center;
gap: 0.5rem;
cursor: pointer;
}
.delete {
background: #ff4444;
color: white;
border: none;
width: 28px;
height: 28px;
border-radius: 4px;
cursor: pointer;
}
</style>
Main TodoApp Component
<script>
import TodoItem from './TodoItem.svelte';
let todos = $state([
{ id: 1, text: 'Learn Svelte', completed: true },
{ id: 2, text: 'Build something', completed: false },
{ id: 3, text: 'Deploy to production', completed: false }
]);
let newTodoText = $state('');
function addTodo() {
if (!newTodoText.trim()) return;
todos = [...todos, {
id: Date.now(),
text: newTodoText.trim(),
completed: false
}];
newTodoText = '';
}
function toggleTodo(id) {
todos = todos.map(todo =>
todo.id === id ? { ...todo, completed: !todo.completed } : todo
);
}
function deleteTodo(id) {
todos = todos.filter(todo => todo.id !== id);
}
let completedCount = $derived(todos.filter(t => t.completed).length);
</script>
This todo app demonstrates several key patterns: component composition with props, reactive state using $state, derived values with $derived, and conditional rendering with {#if}. These patterns scale to much larger applications while maintaining clarity.
Styling in Svelte
Svelte's styling system provides scoped CSS by default, meaning styles defined in a component only apply to that component's HTML. This eliminates the global namespace pollution that plagues traditional CSS development and makes components truly self-contained.
Scoped Styles
Every style rule in a component's <style> block is automatically scoped to that component. You can write normal CSS selectors and properties:
<div class="card">
<h2 class="title">Hello</h2>
<p class="content">Some content here</p>
</div>
<style>
.card {
background: white;
border-radius: 8px;
box-shadow: 0 2px 8px rgba(0,0,0,0.1);
padding: 1.5rem;
}
.title {
margin: 0 0 0.5rem 0;
font-size: 1.5rem;
color: #333;
}
.content {
margin: 0;
color: #666;
line-height: 1.6;
}
</style>
Global Styles
For styles that should apply everywhere, use the :global() selector:
<style>
:global(body) {
margin: 0;
font-family: system-ui, sans-serif;
}
:global(.typography) {
line-height: 1.6;
}
</style>
CSS Variables for Theming
Svelte works naturally with CSS custom properties, enabling easy theming:
<div class="themed-box">
<p>Themed content</p>
</div>
<style>
.themed-box {
--theme-color: #ff3e00;
--theme-bg: #fff5f0;
background: var(--theme-bg);
color: var(--theme-color);
padding: 1rem;
border-radius: 4px;
}
</style>
SvelteKit: Building Full Applications
While Svelte alone handles individual components beautifully, SvelteKit provides the full application framework for building complete websites and web apps. SvelteKit handles routing, server-side rendering, code splitting, and optimized delivery out of the box.
When to Use SvelteKit
SvelteKit is the recommended way to build Svelte applications for most use cases. Choose SvelteKit when you need:
- Multiple pages: File-system-based routing makes creating new pages as simple as adding files to
src/routes - Server-side rendering: Initial page loads are faster and SEO-friendly
- API endpoints: Build backend functionality directly alongside your frontend
- Form handling: Progressive enhancement for robust form processing
- Static generation: Export your site as static HTML for any hosting platform
File-Based Routing
SvelteKit uses directory-based routing. Create pages by adding +page.svelte files to the src/routes directory:
src/
└── routes/
├── +page.svelte # Homepage (/)
├── about/
│ └── +page.svelte # About page (/about)
└── blog/
└── [slug]/
└── +page.svelte # Dynamic routes
Loading Data
Pages can load data for display using +page.js or +page.server.js load functions. The loaded data is available in the page component via $props():
// src/routes/posts/+page.js
export async function load() {
const response = await fetch('https://api.example.com/posts');
return { posts: await response.json() };
}
<!-- src/routes/posts/+page.svelte -->
<script>
let { data } = $props();
</script>
<h1>Blog Posts</h1>
<ul>
{#each data.posts as post}
<li><a href="/posts/{post.slug}">{post.title}</a></li>
{/each}
</ul>
For teams looking to build scalable web applications, our web development services can help you leverage frameworks like SvelteKit for optimal performance and user experience.
Building and Deploying for Production
When your application is ready for production, Svelte's compiler transforms your source code into highly optimized JavaScript. The build process minimizes bundle size, removes development utilities, and optimizes runtime performance.
Build Command
npm run build
This creates a build or dist directory containing your production-ready application. The output includes precompiled HTML, JavaScript, and CSS optimized for delivery.
Deployment Options
SvelteKit applications can deploy to numerous platforms:
- Vercel: Zero-config deployment with edge caching and serverless functions
- Netlify: Automatic deployments from git with form handling and edge functions
- Cloudflare Pages: Global CDN distribution with Workers integration
- Node servers: Self-host on any infrastructure with the Node adapter
- Static hosting: Export as static files for traditional web hosts
For static export, configure your adapter appropriately:
// svelte.config.js
import adapter from '@sveltejs/adapter-static';
export default {
kit: {
adapter: adapter()
}
};
Set prerendering options in a layout to enable static generation:
// src/routes/+layout.js
export const prerender = true;
Development Workflow and Best Practices
Svelte's development experience centers on instant feedback. The Vite development server watches your files and updates the browser in milliseconds whenever you save changes. This works even for complex state--you don't lose your place in the application when code changes.
As applications grow, structure becomes critical. Common patterns include feature-based organization, shared components in a common location, utility functions in src/lib, and TypeScript interfaces in a dedicated types file. For organizations seeking to modernize their technical stack, exploring AI-powered automation services can complement your frontend development efforts and streamline development workflows.
Next Steps and Advanced Topics
You've now built a solid foundation in Svelte development. Several areas merit further exploration as you continue your journey:
State Management Patterns
Beyond local component state, Svelte stores ($state in Svelte 5) enable sharing state across components and handling complex application state. The writable and derived stores work both within and outside components.
Animations and Transitions
Svelte includes powerful built-in transition and animation primitives that make adding polish effortless. The transition: directive creates smooth enter/exit animations, while animate: handles reordering animations for lists.
Testing
Testing Svelte components uses libraries like Vitest for unit testing and testing libraries designed for component testing. Write tests alongside your components to catch regressions early.
TypeScript Integration
While optional, TypeScript provides significant benefits for larger projects. Add it to new projects from the start for better IDE support and compile-time error catching.
Svelte Ecosystem
Explore libraries for common needs--routing (SvelteKit handles this), form handling (sveltekit-superforms), animation (svelte-motion), and more. The ecosystem continues growing with tools for every use case.
When you're ready to take your web applications to the next level, our web development expertise can help you build scalable, high-performance solutions that drive business results.
Frequently Asked Questions
What is Svelte?
Svelte is a compiler-based JavaScript framework that transforms declarative components into efficient imperative code that surgically updates the DOM. Unlike React or Vue, Svelte does its work at build time rather than runtime.
How is Svelte different from React?
React uses a virtual DOM and does work at runtime. Svelte compiles components to vanilla JavaScript at build time, eliminating the runtime overhead associated with the virtual DOM approach.
What are Svelte 5 runes?
Runes are special functions ($state, $derived, $effect, $props) that explicitly declare reactive behavior, replacing the older $: syntax with a more intuitive and consistent system.
Should I use Svelte or SvelteKit?
Use SvelteKit for full applications with routing and server-side rendering. Use standalone Svelte components for simple integrations or widgets within larger applications.
Does Svelte support TypeScript?
Yes, Svelte has first-class TypeScript support. Add it during project creation for type safety throughout your application, with better IDE support and compile-time error catching.
What is the learning curve for Svelte?
Svelte's approachable syntax and minimal abstractions make it relatively easy to learn, especially for those familiar with HTML, CSS, and JavaScript. The compiler-first approach reduces the concepts you need to understand.
Sources
-
Svelte.dev Tutorial - The official Svelte tutorial provides an interactive learning experience covering all fundamental and advanced Svelte concepts
-
MDN Web Docs: Getting started with Svelte - Mozilla's comprehensive guide explaining Svelte's compiler-based approach and project setup
-
LogRocket: How to build a simple Svelte app - A practical tutorial demonstrating how to build a simple Svelte application with code examples
-
Svelte Documentation - Primary reference for Svelte 5 syntax including runes
-
SvelteKit Documentation - Official docs for the SvelteKit application framework
-
Svelte GitHub Repository - Source code and community resources