TIL - How to write a web component with SolidJS

TIL - How to write a web component with SolidJS

Today, I wanted to test the library: SolidJS. It features JSX support and is known for its great performance.

Install the lib

The first step I did was to follow the Getting Started guide and start from a given template:

> npx degit solidjs/templates/ts my-app
> cd my-app
> npm i

Remove the unnecessary files

I removed all content from src/ folder and created an empty index.ts.

This file will serve as an entry point for the lib.

Update Vite's configuration

In vite.config.ts, I added the lib configuration:

export default defineConfig({
  plugins: [solidPlugin()],
  build: {
    lib: {
      entry: path.resolve(__dirname, 'src/index.ts'),
      name: 'my-component',
      fileName: (format) => `my-component.${format}.js`
    },
    // ...
  },
});

With the lib property, Vite provides a way to bundle a library from the given entry point. This library mode still gives you access to the demo page with npm run start.

Creating the component

I then created a file for my component.

// src/MyComponent.tsx

const MyComponent: Component<Props> = (props) => {
   return <> {/*...*/} </>;
}

And I did not export the component like I am used to with react, but I rather registered it with the customElement helper from the solid-element module.

The solid-element is a separate module that needs to be installed.

// src/MyComponent.tsx
import { customElement } from "solid-element";

/* ... */
customElement("my-component", { someProp: "defaultValue" }, MyComponent)

The custom customElement helper works by giving the module a tag name (e.g. "my-component") and binding it to your component (e.g. MyComponent).

You need to pass as a second argument the default props values. Omitting this object prevents the props from receiving data from their corresponding attribute (e.g. some-prop attribute should bridge data to someProp prop).

A note about styles

Vite allows writing css-modules files (e.g. MyComponent.module.css) in order to scope the style to a specific component. Because of the Shadow DOM, styles written in the web component are scoped to the web component by default. That means, in this case, I do not need to rely on the css-modules feature.

In order to add style to my component, I wrote a standard CSS file and import it into the component file.

import styles from "./MyComponent.css";

The styles object is the actual styles string and can be embedded into the web component using the <style> tag.

const MyComponent: Component<Props> = (props) => {
    return <> 
        <style>{styles}</style>
        {/*...*/}
    </>;
}

Because class names are kept untouched by Vite, I can add class names as I would normally do in an HTML markup:

<div class="myComponent" />

Last step

After setting up my component, I imported the component file in the index.ts file.

// src/index.ts
import "./MyComponent.tsx";

I then added the custom web component in the markup of the index.html file (Vite's "demo" page).

<body>
    <my-component some-prop="someValue">
</body>

Everything looked fine. I then built the lib (npm run build) and got three files.

  • style.css: can be ignored because style is also embedded in the JS.
  • my-component.es.js: the ES module output JS
  • my-component.umd.js: the universal module output JS

Both modules seem to work fine when imported into an HTML document, but the UMD is way smaller because minified.

Conclusion

This process is quite straightforward once you know what needs to be configured. I think it is a good starting point, for me, to learn SolidJS and to dip a toe in the web components world.