Skip to Content

How to use Custom Elements with a Framework

drab can be utilized in any framework that supports custom elements.

TypeScript support

If you are using TypeScript, drab includes types for each attribute of each element so you can get autocomplete and type safety just like you would from a framework component. Add a d.ts file within your tsconfig.include paths to extend the types of your framework’s intrinsic elements (examples below).

Server rendering

drab is built to be compatible with server-side rendering (SSR). If you are using SSR, you will need to ensure the element’s code only runs on the client. Below are examples on how to do this in popular JavaScript frameworks with functions like onMount or useEffect. If you aren’t using SSR, you can omit these wrappers since the code will only run on the client.

Examples

*If you see a better way to write any of these examples or a framework that is missing, please create an issue or pull request!

Astro

astro
---
// dialog.astro
import type { DialogAttributes } from "drab/dialog";

const attributes: DialogAttributes = {
	// ...
};
---

<script>
	import "drab/dialog/define";
</script>

<drab-dialog {...attributes}>...</drab-dialog>

Enhance

js
// app/elements/my-dialog.mjs
export default function MyDialog({ html }) {
	return html`
		<drab-dialog>...</drab-dialog>
		<script src="/_public/browser/drab-dialog.mjs" type="module"></script>
	`;
}
js
// app/browser/drab-dialog.mjs
import "drab/dialog/define";

React

tsx
"use client";

// dialog.tsx
import { useEffect } from "react";

export default function Dialog() {
	useEffect(() => {
		import("drab/dialog/define");
	}, []);

	return <drab-dialog>...</drab-dialog>;
}
ts
// drab.d.ts
import type { Elements } from "drab/types";
import type { HTMLAttributes } from "react";

declare module "react" {
	namespace JSX {
		interface IntrinsicElements extends Elements<HTMLAttributes<HTMLElement>> {}
	}
}

Solid

tsx
// dialog.tsx
import { onMount } from "solid-js";

export default function Dialog() {
	onMount(() => {
		import("drab/dialog/define");
	});

	return <drab-dialog>...</drab-dialog>;
}
ts
// drab.d.ts
import type { Elements } from "drab/types";
import "solid-js";

declare module "solid-js" {
	namespace JSX {
		interface IntrinsicElements
			extends Elements<JSX.HTMLAttributes<HTMLElement>> {}
	}
}

Svelte

svelte
<!-- dialog.svelte -->
<script lang="ts">
	import { onMount } from "svelte";

	onMount(() => {
		import("drab/dialog/define");
	});
</script>

<drab-dialog>...</drab-dialog>
ts
// drab.d.ts
import type { Elements } from "drab/types";

declare module "svelte/elements" {
	export interface SvelteHTMLElements
		extends Elements<HTMLAttributes<HTMLElement>> {}
}

export {};

Vue

vue
<!-- dialog.vue -->
<script setup>
onMounted(() => {
	import("drab/dialog/define");
});
</script>

<template>
	<drab-dialog>...</drab-dialog>
</template>
js
// nuxt.config.ts
export default defineNuxtConfig({
	vue: { compilerOptions: { isCustomElement: (tag) => tag.includes("-") } },
});