Svelte Component Libraries: 8 Features Devs Want

I have spent some time testing out a number of component libraries for Svelte to use in projects and have used a few extensively. Based on my experience, I wanted to offer some suggestions for best practices for creating library components. I will try to move from the easier suggestions to more involved ones.

1. Support passing props implicitly with $$restProps

Trying to foresee and define every prop a user may need to pass to a component is a fool’s mission. For example, data attributes for e2e testing are often unique, data-text-xyz. Make things easy and spread implicit props onto the component.

Example 1 REPL

2. Support passing of a class prop

Fortunately, we are not constrained to the same className sort of work-around in Svelte templates we are in JSX in order to avoid clashes with the class key word. You can copy paste any html into a template and it just works. However, we still have the issues in our script tags. If the prop on a component uses the className hack, the nicety of the “just html” in out templates is diminished. There are a few ways to overcome this. You can either use an alias inside the component for the class, such as klass or _class and export it as class, or you can use pass the prop via the $$restProps.class.

Example 2 REPL

3. Prefer slots to props

You may think it better to limit a consumer’s ability to screw up a component by limiting children to explicit string props or, for example, and an icon prop. This will eventually cause someone pain. Perhaps you have a date-picker with an “icon” prop to represent a date or time. But a customer requires a localized text instead of an icon. I have run into a similar issue in a project. With named slots we can allow for any consumer need and there is less need for special-case props. If you need to make a slot for some reason the props passes, this can be done by accessing the $$props.$$slots for default or named slot–not pretty I admit.

In some cases you may want to take the configurability to a higher level by returning a slot as the component with the props spread on it. This is similar to using “render props” in the React world and might be thought of as a headless component, which gives the consumer a lot of flexibility in how the component is realised. It is silly to do this with a button, so here is an example with an accordion.

Example 3 REPL

4. Simply forward DOM events

It is a fairly common practice to use an event dispatcher inside a component to handle events: on:click={e => dispatch('click', { nativeEvent: e })}. The alternative would would be to let the consumer define the handler by just forwarding the event. By ‘forwarding’ I mean adding a bare on:click, which exposes that event on the component. I have never found extra value when a dispatcher is added. It requires extra knowledge about how to access the event object which is passed–if it is passed–as e.g.nativeEvent (for one library I have looked at the docs multiple times to remember this). It is also helpful if the component forwards other less-commonly used but useful events which might be needed for something like prefetching data. Ideally, Svelte would have an api like on:* for forwarding all events but such a syntax does not yet exist. Another alternative to adding every event handler is using an action, something I learned from libraries like Attractions. You pass a list of event handlers and set them up manually with an action. This is a little convoluted in comparison to just forwarding events but has the virtue of not excluding any event. Let’s use this mechanism in our button.

Example 4 REPL

5. Stick to plain css in component libraries

There are a lot of interesting css preprocessors out there and great support with Svelte-preprocess for using them. However, presently most Svelte libraries export components as is, without a compilation step. By using a css preprocessor you force the consumer to add this dependency and probably use variables from that library. This automatically limits who will want to use your components. CSS vars are a great option for handling style tokens in your components.

6. Support dynamic theming

There is on-going discussion in the Svelte repo about the best way to theme and the possibility of adding further support for theming. The problem is that with CSS there are many approaches and options, so it may be a while before any kind of consensus solution is reached, if ever. Still, theming is not difficult in Svelte–we have all the tools needed, mainly reactivity and context. The example below supports both passing a theme as variables variables via context as well as an escape to override the theme at the component level with a style prop. This example has not been tested in production.

Example 6 REPL

7. Support Typescript with SvelteComponentTyped

Typed components is almost de facto in React and Vue libraries. Fortunately support for Typecript for in Svelte is improving all the time. Svelte now exports a class for components that can be used to create a button.d.ts file. Here is a nice example of a button in component library: button type. This type is autogenerated using this tool.

8. Forward actions via prop and actions util

Actions are sometimes the best way to add logic that shouldn’t require a component, such of a focus trap or form validation. They can also be useful in dynamically adding elements to the DOM without needing wrapping divs, such as in the case of a tooltip. However, a limitation of actions is they can only be attached to elements not components. This can be frustrating if you want to, for example, add a tooltip to a button component. You might be forced to wrap the button in a div to attach the tooltip. Libraries can help out by exposing an actions prop that takes a list of tuples of an action with options Array<[action, options]>, passing them to the inner parent element and attaching them with a useActions helper. Here is an example with our button again and a tooltip action. The useActions helper is a simplified version of the one offered by Svelte Material UI.

Example 8 REPL

Let me know what you think

This was a somewhat opinionated list of ideas for making components easy to use and flexible. I would be really pleased to get your input, whether it be a better solution or a contrary opinion. I will amend my post with relevant comments. Write me at [email protected].

Richard Van Camp – Senior Software Engineer
Monad Ltd.