How to use Web Components in Preact and TypeScript
Cory Rylan
- 3 minutes
Web Components provide a reusable UI component model for the Web. Web Components are created using a combination of several stand-alone technologies such as Custom Elements, Shadow DOM, and HTML templates.
Web Components similar to components in Preact communicate with properties and events. Properties can be set to pass data into a component, and custom events allow the component to pass data back up the component tree.
In this post we will learn how to take a simple alert Web Component and use it in our Preact application written in TypeScript.
To use the alert in our Preact component, we import the component and add the x-alert
tag.
import { useState } from 'preact/hooks';
import './alert.js';
export default function App() {
const [show, setShow] = useState(true);
return (
<div>
<button onClick={() => setShow(!show)}>toggle alert</button>
<x-alert hidden={show} status={'status'} closable oncloseChange={() => setShow(!show)}>
This is a Web Component in Preact
</x-alert>
</div>
);
}
If we are using TypeScript, we will likely see errors in our TSX files like this below:
TypeScript cannot type check our TSX template due to it not knowing the CustomElement class reference for our custom element tag. We can add our tag to the JSX.IntrinsicElements
global interface. This will associate x-alert
to the XAlert
class reference.
import { useState } from 'preact/hooks';
import { XAlert } from './alert.js';
import './alert.js';
type CustomEvents<K extends string> = { [key in K] : (event: CustomEvent) => void };
type CustomElement<T, K extends string = ''> = Partial<T & { children: any } & CustomEvents<`on${K}`>>;
declare global {
namespace preact.createElement.JSX {
interface IntrinsicElements {
['x-alert']: CustomElement<XAlert, 'closeChange'>;
}
}
}
With Template Literal Types, we can create a list of string types and generate a new list of string types with the on
prefix for our events. Next we can pass the event strings to the CustomEvents
type, which will adds the function signature . Now we have type checking with custom events for our Web Components in Preact and TSX.
import { useState } from 'preact/hooks';
import { XAlert } from './alert.js';
import './alert.js';
// auto-generate these types using: https://github.com/coryrylan/custom-element-types
type CustomEvents<K extends string> = { [key in K] : (event: CustomEvent) => void };
type CustomElement<T, K extends string = ''> = Partial<T & { children: any } & CustomEvents<`on${K}`>>;
declare global {
namespace preact.createElement.JSX {
interface IntrinsicElements {
['x-alert']: CustomElement<XAlert, 'closeChange'>;
}
}
}
export function App() {
const [show, setShow] = useState(true);
return (
<div>
<button onClick={() => setShow(!show)}>toggle alert</button>
<x-alert hidden={show} status={'success'} closable oncloseChange={() => setShow(!show)}>
This is a Web Component in Preact
</x-alert>
</div>
);
}
With TypeScript Generics and Template Literal Types, we can create an excellent developer experience using Web Components in Preact. If you maintain a Web Component library check out my library custom-element-types which generates type definitions for various frameworks as described in this blog post automatically. Check out the demo below!