little cubes

What I learned from publishing a Svelte library

Zach Posten - 2022 Jul 29

The highs, the lows, the lessons learned

What I learned from publishing a Svelte library

I recently published the first version of Formsvelte, a complete form solution for Svelte; with an API inspired by Formik.

Formsvelte is the first library that I've created for the ever-growing Svelte ecosystem, so I thought I would share some of my takeaways from that experience.

1. SvelteKit takes all the work out of figuring out how to bundle a library

Usually when you first sit down to write a new library, your first hour or two goes into configuring Rollup or some other bundler in a way that suits your needs. I started doing that, only to find out that SvelteKit has built in support for packaging a library! 🎉

2. You can use named exports, just not in the most obvious way

I wanted my library to have named exports so that users could import all my components from a single path:

import {MyComponent} from 'mylib'

A couple different resources that I found online mention "re-exporting" your components, but it was unclear if I needed an index.ts file or an index.svelte file. I wasn't sure how it would work with an index.svelte file and renaming the default during export didn't work either:

// This doesn't work for a Svelte library! export {default as MyComponent} from './my-component.svelte'

I eventually found that the solution is to first default import your components, and then export them; you can't do both in one step.

1 2 3 4 5 6 7 // src/lib/index.ts import MyComponent from './my-component.svelte' import MyOtherComponent from './my-other-component.svelte' // Re-export Svelte components from your library export {MyComponent, MyOtherComponent}

3. You can't pass down stores as slot props

I wanted to create a store within the context of a wrapper component and then pass that store down to child components via a slot prop. My thinking was that this could be like Svelte's version of React's render props:

1 2 3 4 5 6 7 8 <!-- Wrapper.svelte --> <script> const val = writable(0) const context = {val} setContext('mylib', context) </script> <slot val={val} />
1 2 3 4 <!-- EndUser.svelte --> <Wrapper let:val> {$val} </Wrapper>

It turns out though that that's not possible. There is this bug on the topic (that was closed in favor of other bugs that seem slightly different to me, but I'm sure the maintainers know what they're doing), and you also get this linting error when you try to do so:

Stores must be declared at the top level of the component (this may change in a future version of Svelte) svelte(contextual-store)

I'm hoping (as the linting error foreshadows) that this does change in a future version of Svelte. But in the mean time, I still haven't figured out how to get around this. It looks like it might be possible to implement Svelte render props as a clunkier version of what I was trying to do with slot props, so that will be the next road I explore.

4. Passing CSS classes to components is harder than it should be

In my experience with building reusable components in JavaScript frameworks, I've found that one of the most important attributes is to make them easy to style. The primary mechanism for doing so is typically passing around CSS classes. There are two attributes of Svelte that make this difficult.

  1. Svelte has chosen to follow HTML in using class="" to declare CSS classes, rather than following the React path and using className="". This is problematic in a JS framework of course because class is a reserved keyword in the language. In terms of Svelte, that means to allow passing a class to a component, it requires two statements instead of the usual one:

    let className: string | undefined = undefined export {className as class}
  2. You can't pass class names declared in a Svelte component to a child component without making it global. This seems silly to me. With CSS modules it is perfectly fine to declare a class name in a particular module, import it into a component, and then pass that imported class down to any arbitrary child component. With Svelte's locally scoped class names, the same is not true.

    • This functionality is described nearly verbatim (I swear I wrote the above before searching GitHub) in an extremely popular GitHub issue
    • One of the Svelte maintainers (unfortunately) responded in that thread closing the issue and saying that they're never going to implement it
    • There is a Svelte RFC for adding CSS custom properties to components. Though I don't fully understand yet why this has been determined to be a better solution or when/if it will ever be implemented?

5. Svelte is really cool

If you haven't tried it yet, take some time and thoughtfully go through the Codecademy style tutorial that the Svelte team has clearly put a bunch of time into creating. It's a really delightful experience and gets you up and running quickly with all the "quirks and features" of the framework.