Elijah Asaolu I am a programmer, I have a life.

Build web components with Svelte

6 min read 1941

Build Web Components With Svelte

Introduction

Every developer should be concerned about reusability and separation of concerns in code since they help preserve formality across our project and separate the business logic from the app infrastructure.

Web components take this one step further by allowing us to create reusable components that are isolated from the rest of the code.

This article will walk you through the process of creating universal web components with Svelte. Universal in the sense that the component is not limited to your Svelte application alone, but can also be reused in any JavaScript application (Vue, React, etc.). We’ll also go through some of the major drawbacks of using Svelte to create web components.

First, let’s learn more about web components.

What are web components?

Web components allow us to create reusable, custom HTML elements with encapsulated styling and functionalities.

Assume we have an extensive HTML code to create a navbar:

<style>
/* CSS code for our navbar */
</style>
<navbar>
<!-- Some long code for our navbar -->
</navbar>

With web components, we can define a custom element (e.g., <custom-navbar />) along with the markup above and reuse it anywhere in our application without the styling applied to this component clashing with the rest of our application. This is possible with the shadow DOM.

What is the Shadow DOM?

Shadow DOM is a smaller, self-contained DOM that is ​​rendered separately from the main DOM, allowing us to isolate both styling and markup behavior to just a single component. Shadow DOM essentially allows us to keep component features private so that they can be styled and scripted without interfering with the rest of our application’s appearance.

Now that we know more about web components, we can begin building them for our Svelte application.

Building your web components

Prerequisites

To follow along with this tutorial, these prerequisites are required:

We made a custom demo for .
No really. Click here to check it out.

  • Basic familiarity with HTML, CSS and JavaScript
  • Familiarity with CLI (terminal/command prompts)
  • A text editor
  • Some familiarity with Svelte is also a plus, but not required

Getting started

For our tutorial, we will work on creating two components:

  • The first is a simple card component that accepts three props: the card title, description, and a card image. And this component will be re-usable anywhere via <my-card />
  • The second is a styled button that accepts a single prop type that allows us to choose between a solid and an outlined button. The custom name for our button component will be <cool-button />, which will also be accessible outside of Svelte

We’ll also look at how to bundle these components together and export them as a single file, as well as how to generate a separate file for each component.

The following is a final preview of how our components would look when they are instantiated (depending on the props provided):

Final Preview Of Instantiated Components

We’ll begin by creating a new Svelte application and installing the necessary packages:

npx degit sveltejs/template web-component-tut
cd web-component-tut
npm install

Once our new Svelte app has been created, run the following command to launch our new app in the browser:

npm run dev

The command above will launch our new Svelte application at http://localhost:8080 (or any other available port, if 8080 is already in use), and when we visit the URL, we should see the welcome page shown below:

The Welcome Page Of The New Svelte Application

Building a component

The process of generating a universal web component with Svelte is similar to how you would create a regular Svelte component, except with a few modifications.

To create the card as a Svelte component, for example, we’ll need to first create a file src/Card.svelte and define the component props, styling, and markup like below:

<script>
  // component props
  // Camel case not supported for props, see drawback section.
  export let card_title, card_desc, card_img;
</script>

<main>
  <div class="card-container">
    <div class="card">
      <img src={card_img} alt="My product" />
      <div class="card-body">
        <div class="row">
          <div class="card-title">
            <h2>{card_title}</h2>
          </div>
        </div>
        <p>
          {card_desc}
        </p>
        <button>Do Something</button>
      </div>
    </div>
  </div>
</main>

<style>
 .card {
    max-width: 350px;
    border-radius: 5px;
    box-shadow: 0 4px 6px 0 #00000033;
    padding: 0 0 10px 0;
  }

  .card img {
    width: 100%;
    height: auto;
  }

  .card-body {
    padding: 5px 10px;
  }

  .card-body p {
    color: #575757;
    margin-bottom: 20px;
    font-size: 14px;
  }
</style>

And, generally, we’ll be able to import it into other Svelte components and render it as follows:

<script>
  import Card from "./Card.svelte";
</script>

<main>
  <Card
    card_title="My Card Title"
    card_desc="Lorem ipsum dolor…"
    card_img="path/to/my-image.png"
  />

</main>

The same process applies to our button component. We start by creating a /src/Button.svelte file, with the code for our styled button:

<script>
  // Component props
  export let type = "solid";
</script>

<button class={type == "solid" ? "btn-solid" : "btn-outline"}>
  <slot />
</button>

<style>
  button {
    padding: 10px;
    color: #fff;
    font-size: 17px;
    border-radius: 5px;
    border: 1px solid #ccc;
    cursor: pointer;
  }
  .btn-solid {
    background: #20c997;
    border-color: #4cae4c;
  }
  .btn-outline {
    color: #20c997;
    background: transparent;
    border-color: #20c997;
  }
</style>

And we can also reuse in other Svelte components like below:

import Button from "./Button.svelte";

<Button type="outline">Click me</Button>

Converting a custom component to a universal component

Converting these custom Svelte components to a universal component that can be used across other frameworks and libraries is a pretty straightforward process.

To begin, we’ll need to add an entry in the Svelte config file that allows us to generate custom elements. To do so, open up rollup.config.js and, under plugins export, add an entry for customElement, under compilerOptions, setting its value to true so that your rollup config file looks like this:

...
 plugins: [
    svelte({
      compilerOptions: {
        dev: !production,
        customElement: true,
...

After you’ve made the necessary changes to the config file, we’ll need to give our tag a unique name. Open the Card.svelte file we created earlier and add the following code to the file’s first line:

<svelte:options tag="my-card" />

The value of the tag attribute above represents the custom tag for our component.

We’ll need to follow the same process for our button component. Open up Button.svelte and add the following line to the beginning of this file:

<svelte:options tag="cool-button" />

The final step is to import our custom components in the Svelte main.js file so that they are generated at build time. To do this, open /src/main.js and replace it with the following code:

import Button from "./Button.svelte";
import Card from "./Card.svelte";

At this point, we have completed all of the steps required to create our custom element. The next step is to generate the bundle file, which will allow us to use this component in any other web application.

Run the following command from your CLI/terminal to generate the bundle file:

npm run build

This will generate two files, build.js and build.map.js, inside the /build folder in the root directory of our project. build.js is the bundled JavaScript code needed to render our component, and build.map.js is the source map for build.js.

You can ensure that everything went smoothly by copying the bundle.js file to a new folder and creating a new index.html file with the following code:

<!DOCTYPE html>
<html>
  <head>
    <title>My website</title>
    <script src="./build.js"></script>
  </head>

  <body>
    <div class="container">
      <div class="row">
        <div class="col">
          <my-card
            card_title="Red Person"
            card_desc=" Lorem ipsum dolor sit, amet consectetur.."
            card_img="https://bit.ly/34B3zHX"
          >
          </my-card>
          <!-- Image credit - Shubham Dhage on unsplash.com -->
        </div>
        <div class="col">
          <div class="border-bottom py-5">
            <cool-button> Solid Cool Button </cool-button>
            <cool-button type="outline"> Outlined Cool Button </cool-button>
          </div>
        </div>
      </div>
    </div>
  </body>
</html>

The code above is simply a standard HTML page that includes our component, and when we execute it, we should see our components displayed on the page as shown below:

Components Displayed On Webpage

Components splitting

In some cases, we do not want all of our components to be generated in a single build file. Sometimes we want to generate them individually. And this is very much feasible by modifying the rollup.config.js input and output exports to meet these requirements.

Our input exports will be an array of component paths, and the output will be a build directory rather than a single build file:

export default {
  input: ["src/Card.svelte", "./src/Button.svelte"],
  output: {
    format: "iife",
    dir: "public/build/",
  },
...

And if we run npm run build again, our component files will be generated for us separately as Button.js and Card.js in the public/build folder.

We can then link them individually in a markup to render our components like below:

<script src="Button.js" type="module"></script>
<cool-button type="outline">Click Me</cool-button>

<!-- another-page.html -->
<script src="Card.js" type="module"></script>
<my-card card_title="..."></my-card>

Major drawbacks

We’ve just learned how to create web components with Svelte, and while the process is inarguably an easy one, there are some drawbacks attached to using Svelte for web components, and some of them are mentioned below.

  • No camel case in components props — Due to some bugs, declaring your component props in camel case will not work, and this could be a deal-breaker, given that camel case is the standard for declaring variables in JavaScript. But fortunately for Vite users, a workaround plugin was recently created to address this
  • Web components cannot be reused in Svelte without tagging them — Unfortunately, you’ll also have to tag every Svelte component you want to use in a custom web component

If we have a Header.svelte file that we want to export as <my-header /> and this component relies on another Nav.svelte file that we did not want to export, this drawback requires that we also tag our Nav.svelte file even though we do not want to export it:

// Nav.svelte
<svelte:options tag="my-nav">
<!-- Code for navbar -->

It would throw an error otherwise. There is also a fix for this as described here, but it would be great if issues like this were fixed out of the box.

  • Browser support — The JavaScript customElement API, which is used in the background to create web components, is not currently supported by all browsers. Polyfill can be used as a fix for this, and the webcomponents official polyfill is a great place to get started

Conclusion

In this article, we learned how to use Svelte to create a universal card and button component, generate the bundle file, split them, and even reuse this component in a separate HTML page.

If you’re interested in learning more about web components and the Svelte framework, check out the resources below:

: Full visibility into your web apps

LogRocket is a frontend application monitoring solution that lets you replay problems as if they happened in your own browser. Instead of guessing why errors happen, or asking users for screenshots and log dumps, LogRocket lets you replay the session to quickly understand what went wrong. It works perfectly with any app, regardless of framework, and has plugins to log additional context from Redux, Vuex, and @ngrx/store.

In addition to logging Redux actions and state, LogRocket records console logs, JavaScript errors, stacktraces, network requests/responses with headers + bodies, browser metadata, and custom logs. It also instruments the DOM to record the HTML and CSS on the page, recreating pixel-perfect videos of even the most complex single-page and mobile apps.

.
Elijah Asaolu I am a programmer, I have a life.

Leave a Reply