DEV Community

Cover image for Standalone SvelteKit Component Development w/o Storybook
Dirk Porsche
Dirk Porsche

Posted on

Standalone SvelteKit Component Development w/o Storybook


Create a "components" route with a page route for every component you like to view and interact with independently. Add a layout to the "components" folder. That has a toc, to allow navigating from one component to the next, and use "dev" from "$app/env" to allow access to the components only during development.


As soon as your little webapp project leaves the simplest stage, it really get's annoying to navigate multiple "pages" or trigger certain events to get to the one component you are currently developing.

Sure you can use "unit" tests to validate that the component behaves correctly and that it displays certain things. But if you are more the visual type, like me, then you want to actually see and interact with the component.

In the past I used Storybook for that. With mixed feelings. It did what it was supposed to do: let you develop components in isolation. But since it was trying to be usable across multiple (if not all) frontend frameworks it has a huge dependency tree.

I decided to pivot to SvelteKit for my current project and Storybook has currently a bunch of issues with SvelteKit. Focussing on the main purpose of Storybook I decided to emulate Storybook with native SvelteKit functionality.


You can find a full working repository with a single component example over at Github.

I have started from the SvelteKit "skeleton" template, created like documented here.

First I created a "lib" directory and added a very simple "Button.svelte" component, which exposes only one prop, that let's the component user set the text of the button.

  export let text = ""

Enter fullscreen mode Exit fullscreen mode

Then I added a "components" folder in the "routes" folder. In the "components" folder I added a layout file (__layout.reset.svelte) that resets the root level layout (which certainly might exist in a real project) so that we have a fully independent thing. I used a flex layout to have the toc on the left side and the actual component container on the right side. Both filling the complete vertical height. I will spare you the code here. It's actually nothing special.

Next I added a little script in the layouts file, that acts as a gate, if you will. If the server is not started in dev mode the layout returns 404 and never displays a component.

  import { dev } from '$app/env';

  export const load = ({page}) => {
    if (!dev) {
      return {
        status: 404,
        error: new Error(`Could not load components url`)
    return {}
Enter fullscreen mode Exit fullscreen mode

Then, to actually display the button component, I added a "button.svelte" route in the "components" folder loading the component and instrumenting it:

  import Button from '$lib/Button.svelte'
  let text = "Click me!"

<Button {text}/>
Enter fullscreen mode Exit fullscreen mode

And I put a link into the toc section of the layout file that points to the button component route:

<div class="wrapper">
    <a href="/components/button">Button</a>
  <div class="component-wrapper">
Enter fullscreen mode Exit fullscreen mode

That's it, basically


You might say: "But Storybook is so much mure, etc. pp." Well. Probably. But working on a component independently was the most important feature for me. I don't remember using anything else regularly. So why including that behemoth, even if it would be working correctly.

Another point might be, that the way I implemented the "dev-gate" is probably suboptimal. One should try to ship only code that can be accessed. Which is totally true (or maybe not, since you ship regularly unused/unaccessible code when working with feature flags).

Nevertheless I haven't bothered so far to look into the bundle, that get's build by "svelte build". But actually it wouldn't surprise me if the button component is left out, when it's not used anywhere else, because Svelte is so intelligent ... nah ... it would surprise me. But hey, that's actually an optimization, you can attack later by filtering the "components" route when bundling for production.

Top comments (0)