Custom Elements: The Superpower of Svelte 5

Custom Elements: The Superpower of Svelte 5

Svelte 5 and the Future of Truly Reusable Web Components

In today's web development ecosystem, framework fragmentation can be a headache. Can you imagine creating components that work equally well in React, Vue, Angular, or even plain HTML? With Svelte 5 and Web Components technology, this isn't just possible—it's surprisingly simple.

The Challenge

Modern frontend development is based on reusable components. However, these components often remain "trapped" within the ecosystem for which they were created. A button made in React will hardly work in a Vue application without significant modifications.

In this article, we'll explore how to create truly framework-agnostic components using Svelte 5 and Storybook 8+. Our example will be a fully customizable Alert component that will work in any framework or environment—in some cases directly, in others with minimal modifications.

What's the Problem?

Imagine this situation: your company has several teams working on different projects. Team A uses React, team B uses Vue, and new project C is being developed with Svelte.

If each team creates their own components from scratch:

  • Effort is duplicated
  • Visual and behavioral inconsistencies emerge
  • Maintenance becomes a nightmare
  • The learning curve multiplies

Furthermore, if tomorrow they decided to migrate to another framework, they would have to rewrite all the components. This represents an enormous cost in time and resources.

How to Solve It: Web Components with Svelte 5

The magic of our solution lies in the combination of Svelte 5 with Web Components (or Custom Elements) and their documentation in Storybook 8+.

  1. The Alert component in Svelte
  2. Documentation with Storybook
  3. Detailed documentation with MDX (to be covered in another post...)

Let's look at the code for our Alert.svelte component:

Article content

Breaking down the code:

The magic is in this line: <svelte:options customElement="garnet-alert" /> This directive converts our Svelte component into a Web Component that can be used in any framework or plain HTML. When you compile this component, Svelte generates a JavaScript file that, when included in any project, registers the custom element <garnet-alert> which you can use like any other HTML tag.

The rest of the code consists of the $props() directive, which automates reactivity. This reactive directive allows you to declare and destructure all the properties that the component can receive from the outside, assigning them default values, very similar to a React prop. Then there are a couple of functions: getIconSVG() to get the corresponding icon from the svgIcons object, and finally the closeAlert() function to close the notification dialog box.

Let's look at the HTML code of the component:

Article content

The structure of a Svelte component consists of the JavaScript/TypeScript script that provides functionality, the HTML which is the rendering template, and the associated style either in CSS within the same file or using tailwindcss among others. The Shadow DOM will always ensure that the CSS belongs exclusively to the defined component. In this case, it's omitted. Basically, we have a dialog tag whose style varies depending on the type of icon (info, warn, error, success, dark, default), which provides the appropriate style. Inside, there are the title and description props that will be displayed in the notification, as seen in the following image:

Article content

Documenting with Storybook

The configuration of Storybook version 8+ in a Svelte v5 project created via Vite is actually automatic, but you can refer to the official documentation for installation at StoryBook Docs, Installation. What we have then is a file with the extension Alert.stories.js, which tells the project and StoryBook where the component comes from and how to render it and add component variations. It's also possible to include a table of properties that can be modified live to observe the component's behavior, as shown in the example code.

Article content

The heart of our file begins with export default {...}. This is like the main menu of a restaurant: it tells Storybook all the basic information about our component. The title is super important—it's like your home address but for Storybook. In this case, we're saying: "Hey, place this component in the 'Garnet UI Library' folder, inside 'Notification Components', and call it 'Alert'". When you open Storybook, you'll see exactly that folder structure in the left sidebar menu. Inside that export default, we find argTypes. This section is like the instruction manual for our component.

Each property here defines:

  • What type of control Storybook will display (checkbox for booleans, text field, selector, etc.)
  • A friendly description so other developers understand what it's for
  • Default values and additional configuration

The table part is like adding a detailed price tag—it shows technical information in Storybook's properties table. Then we have parameters, which are like the extra menu configuration; Here we're saying: "The general description of the component is this" and "when showing examples, give them a height of 150px". It's like giving additional instructions to the chef on how to present the dish. In this case, the chef is Storybook.

Now come the most interesting parts: the stories. Each export const is like a visual example of our component in a specific situation:

  • render: This is the function that says "this is how I want to display my component". In Svelte, we use this standard pattern.
  • args: These are the specific ingredients for this version of the component. Here we're saying: "Show an alert of type 'info', with this title, this description, etc."

Each story (Default, Warn, Error, etc.) shows a different variant of the same component, like photos of the same furniture in different colors so the client can choose.

Article content

In Summary

The Storybook file is like an interactive catalog for our Alert component:

  1. First, we define where to find it in the menu (title)
  2. Then we explain all its customizable options (argTypes)
  3. We add extra configuration if needed (parameters)
  4. And finally, we show practical examples of all its variants (stories)

This way, any developer using our component can:

  • See how it looks in different states
  • Play with its properties to see how it changes
  • Read the documentation to understand how to use it

And all this without having to read a single line of the component's internal code. It's like test-driving a car without needing to understand how the engine works!

What Makes These Components Framework-Agnostic?

The key to understanding why our component is agnostic lies in Web Components (or Custom Elements) technology. Web Components are a set of standard web technologies that allow creating custom, encapsulated, and reusable HTML elements. When Svelte compiles a component with the customElement directive, it generates a Web Component that:

  • Encapsulates its style and functionality: Styles defined within the component don't affect the rest of the page.
  • Exposes a clear API: The component can be used through standard HTML attributes.
  • Is compatible with any framework or pure HTML: It doesn't depend on Svelte to function, meaning in the Svelte framework it would be native and used without modifications, and in frameworks that support web components like Astro, SolidJS, among others, the modifications are minimal or none.

In frameworks that have their own engine, their lifecycle is different, especially those using a reconciliation algorithm for their Virtual DOM; more code needs to be created to adapt the requests or states specific to the framework regarding the functionality established in the Web Component.

How to Use Our Component in Different Environments?

Once compiled, our component can be used like this:

In plain HTML:

Article content

In React:

Article content

In Vue.js:

Article content

In SolidJS:

Article content

It's that simple! Web Components are used like any standard HTML tag in both frameworks. However, as I mentioned before, if a component has more complex functional logic, for example, returning a state that needs to be obtained to perform other logic, in these types of scenarios is where the use in React and Vue.js becomes complicated since you need to add functionalities compatible with their engines, as the philosophy of state management is specific to each engine.

Advantages of Web Components with Svelte

  • Write once, use everywhere: The same component works in any framework or without a framework.
  • Optimized performance: Svelte compiles the code to highly efficient vanilla JavaScript.
  • Real encapsulation: Styles and functionalities don't interfere with the rest of the application.
  • Easy maintenance: A single component to maintain instead of multiple implementations.
  • Long-term compatibility: By following web standards, components are more resistant to changes in frameworks.
  • Reduced size: Svelte generates very compact code, resulting in lighter components.

Disadvantages

  • Browser support: Although most modern browsers support Web Components, there may be issues in older browsers.
  • Custom Elements limitations: Some advanced functionalities specific to each framework may not be available.
  • Initial learning curve: Understanding Svelte's mental model and Web Components takes time.
  • Less mature ecosystem: Compared to React or Vue, the Svelte and Web Components ecosystem is more recent.

How to Overcome Web Components Disadvantages

Browser support Solution: Use polyfills for compatibility with older browsers.

Custom Elements limitations Solution: Design a clear event API and use standard communication patterns.

Less mature ecosystem Solution: Leverage existing libraries and contribute to the ecosystem.

  • Share your components as open source to enrich the ecosystem
  • Join communities like Web Components Community Group
  • Implement good testing and documentation practices for your components

Ease of Implementation by Framework

From easiest to most difficult:

  1. Vanilla HTML: Simply import the script and use the component.
  2. Svelte: Native compatibility and perfect integration.
  3. Astro: Excellent support for Web Components.
  4. SolidJS: Natively supports Web Components.
  5. Vue: Good integration with Custom Elements.
  6. React: Requires some adjustments to handle events and properties.

Why Architects Should Consider This Solution

As a software architect or technical leader, there are powerful reasons to consider Web Components in Svelte:

  • Visual and functional consistency: Ensures that all teams use the same components, regardless of the framework.
  • Technical debt reduction: Less duplicate code means fewer bugs and easier maintenance.
  • Technological flexibility: Teams can change frameworks without needing to rewrite the basic components.
  • Team scalability: Different teams can work on different parts of the application using different technologies while maintaining a coherent user experience.
  • Future-proof: Being based on web standards, these components are more likely to remain relevant in the future.

Conclusion

The combination of Svelte 5 and Web Components represents a revolutionary approach to user interface development. It allows creating truly agnostic components that work in any environment, reducing duplication of efforts and ensuring a consistent user experience. Additionally, incorporating Storybook as a documentation tool significantly elevates the value of these components. By documenting each variant, property, and possible behavior, Storybook not only facilitates the use of components for other developers but also serves as a visual and functional source of truth. This living documentation allows teams to test, visualize, and understand how components work without needing to read their internal code, accelerating adoption and reducing the learning curve. Ultimately, a well-documented agnostic component with Storybook becomes a high-value asset that transcends frameworks and teams, materializing the true potential of reuse in modern frontend development.

Although it's not the perfect solution for all use cases, it's a powerful option that architects and developers should seriously consider, especially in organizations with multiple teams or projects using different technologies.

What did you think of this article on Svelte 5 and Web Components? We'd love to hear about your experience implementing these solutions in your projects. Have you found other advantages or challenges not mentioned? This agnostic component approach is revolutionizing the way we build interfaces, and we've barely scratched the surface. In upcoming articles, we'll delve into topics such as advanced documentation integration in Storybook, Web Component performance in large-scale applications, and how to build complete design systems with this architecture. We'll also explore npm deployment and how to perform tests in Storybook. Stay connected to discover how to take your components to the next level, and don't hesitate to share this article with other developers interested in building more efficient and coherent interfaces!

To view or add a comment, sign in

More articles by Lenin Uzcategui

Insights from the community

Others also viewed

Explore topics