Article

So You Want to Build a Svelte App From Scratch (And Not Regret It Later)

2025-12-096 min read
SvelteWeb DevelopmentFrontendTutorial
Building a Svelte App illustration

There's a particular kind of person who chooses to build a Svelte app from scratch. Someone who has looked React dead in the eye — specifically the part where it asks you to memoize a callback, wrap your component in forwardRef, then sacrifice a woolly yak to the Reconciliation Gods—and said, "You know what? No. I want joy in my life."

Svelte is the framework for people who enjoy actually writing code, not negotiating with it. While React is busy lecturing you about purity, immutability, and how you really should add just one more hook, Svelte hands you a warm cup of tea and whispers, "Just write a <script> tag. It's fine." Vue is charming but sometimes feels like you've been handed a full arts-and-crafts kit when all you needed was a pencil. Angular… Angular is that friend who carefully labels every moving box with a 42-item metadata schema "because it's best practice."

Svelte's trick is simple: it compiles your components into tiny, hyper-optimized JavaScript. No heavy runtime, no ghostly re-rendering puzzles, no "why does this hook exist?" spirals. You write straightforward components; Svelte quietly performs sorcery behind the curtain.

Use Svelte when you want a framework that's fast, intuitive, and unlikely to make future-you curse past-you's architectural choices. Use something else when you enjoy suffering. That's the whole spiritual arc.

And with that spiritual clarity, let's build something worthwhile.

1. Kickstarting the Project

Starting a new Svelte project is refreshingly simple, almost suspiciously so:

npm create svelte@latest my-app

cd my-app

npm install

Fire it up:

npm run dev

Head to http://localhost:5173 and admire the default counter component — Svelte's way of saying, "Welcome, enjoy your stay, please don't delete me too quickly."

Small thought: Every new project begins with a counter. It's the universe's way of saying: "You will increment things… You will increment many things."

2. Establishing a Sensible Project Structure

The default SvelteKit layout is fine until the moment it isn't. If you've ever opened an old project and thought, "Why did I put this component here? Was I okay?" — This section is for you.

src/
  lib/
    components/
      ui/
      layout/
      domain/
    routes/
    stores/
    utils/
  app.d.ts
  hooks.server.ts
  hooks.client.ts
tests/
storybook/

Quick rationale:

  • components/ui — tiny presentational pieces; the LEGO bricks
  • components/layout — navigation, structure, framing; the IKEA shelves
  • components/domain — business logic, where the "actual job" happens
  • utils — functions that behave like good citizens
  • stores — global-ish state without needing Redux or incense rituals

Architecture isn't religion; adjust this as the app evolves. The goal is simply to avoid waking up three months later and whispering, "Why did I put this component here?"

"A messy file structure is like a messy bedroom — eventually you just start throwing things on the bed and pretending it's fine."

3. Adding Paraglide for Localization

(Optional — only if your app needs multiple languages)

If your app will forever exist in one language, skip this with a clean conscience. But if your team or product manager utters the phrase, "We might support other regions," you'll want Paraglide sooner rather than later. It gives you type-safe, human-friendly localization without drowning you in config.

Install:

npm install @inlang/paraglide-js

Initialize:

npx paraglide init

Example translation:

messages/en.json

{
  "greeting": "Hello, {name}!"
}

Use it:

<script lang="ts">
  import { m } from '$lib/i18n/messages';
</script>

<p>{m('greeting', { name: 'JJ' })}</p>

Paraglide keeps things type-safe and straightforward, so you don't end up debugging typos in translation keys at 2 a.m., whispering "Why aren't you working?" into your monitor.

4. Introducing Storybook v9 — A Playground for Grown-Ups

(Optional — use when you want isolated component development or a design system)

Storybook 9 is a peaceful little world where your components live independently, free from global styles, routing, or application chaos. If you're building a big UI, collaborating with designers, or creating a reusable component library, Storybook pays for itself. If you're building a simple app and speed is king, skip it and revisit later.

Initialize:

npx storybook@latest init

Storybook 9 automatically sets you up with the new builder, zero-config Vite support, and a cleaner folder structure. No need to specify builders manually anymore — Storybook finally stopped asking you 14 questions just to render a button.

A tiny Svelte component:

Button.svelte

<!-- Button.svelte -->
<script>
  export let label = "Click me";
</script>

<button>{label}</button>

A Storybook v9 story:

import Button from './Button.svelte';

export default {
  title: 'UI/Button',
  component: Button,
};

export const Primary = {
  args: { label: 'Hello Storybook 9!' }
};

Start it:

npm run storybook

"Storybook is like a digital terrarium for your components — perfect lighting, controlled environment, no predators (a.k.a. global CSS)."

5. Testing with Playwright + Storybook Runner

(Optional — choose this when you need proper UI or E2E testing)

Testing is wonderful when you need it and completely unnecessary when you don't. If your app is small or internal, feel free to skip this. If it's customer-facing, long-term, or fragile, give your future self the gift of automated tests.

Install Playwright:

npm install -D @playwright/test
npx playwright install

Install Storybook's test runner:

npm install -D @storybook/test-runner

Example test:

import { expect, test } from '@playwright/test';

test('button renders', async ({ page }) => {
  await page.goto('http://localhost:6006/iframe.html?id=ui-button--primary');
  const button = page.locator('button');
  await expect(button).toHaveText('Hello Storybook 9!');
});

"Think of testing as brushing your teeth — you can skip it, but eventually something regrettable happens."

6. Component Testing Inside Storybook

(Also optional, but extremely cool)

Storybook "play functions" let components test themselves directly inside their stories — like documentation that proves they work.

export const Primary = {
  args: { label: 'Click me' },
  play: async ({ canvasElement }) => {
    const canvas = within(canvasElement);
    const button = await canvas.getByRole('button');
    expect(button).toBeInTheDocument();
  }
};

It's weirdly meta, but wonderfully reliable. Your story becomes both demo and test, like a recipe that cooks itself.

7. Bringing It All Together

By now, depending on what you chose to include, you may have:

  • a clean, maintainable SvelteKit architecture
  • multilingual support with Paraglide
  • a Storybook-powered component environment
  • Playwright tests catching UI surprises
  • story-level component tests that verify behavior automatically

Whether you used all of them or just one, your Svelte app is now clearer, sturdier, and far kinder to future-you.

8. Wrapping Up

Building a Svelte app from scratch doesn't need to be dramatic. With a tidy project structure, optional extras like Storybook, Playwright, and Paraglide, and Svelte's naturally pleasant developer experience, you end up with an app that's fast, maintainable, and genuinely enjoyable to work on. Start small, add tools only when you need them, and let Svelte stay what it does best — simple, efficient, and refreshingly low-stress.

Conclusion

Svelte has a way of reminding us that building for the web doesn't have to feel complicated or exhausting. With a clean foundation and carefully chosen tools, you can create apps that are fast, friendly, and genuinely enjoyable to maintain. Keep experimenting, keep refining, and let your stack grow only as much as your project truly needs. The web is big, strange, and full of possibilities — and you now have a setup that's ready for all of them.

Here's to building things you're proud of — and enjoying the process along the way.