Installation
Install the package
Section titled “Install the package”npm install @golemui/gui-components litlit is a peer dependency, version 3.3.1 or later.
Load the stylesheet
Section titled “Load the stylesheet”Import the stylesheet once for your whole app, for example in your entry file:
import '@golemui/gui-components/index.css';It holds the design tokens and the styles of every element. To load the tokens on their own, or to add the optional Clay theme, see Stylesheets.
Register the elements
Section titled “Register the elements”Each element has its own entry point, which registers that element and nothing else:
import '@golemui/gui-components/textinput';import '@golemui/gui-components/date-picker';Then use the tags anywhere HTML goes:
<gui-textinput label="Email" name="email" required></gui-textinput>To register every element at once, import the package itself:
import '@golemui/gui-components';Each component page shows its entry point and the code for every framework.
Client-side rendering
Section titled “Client-side rendering”@golemui/gui-components/react has a component for every element, with typed props and an
on* prop for each event. It needs react 18 or later.
import { GuiTextinput } from '@golemui/gui-components/react';
<GuiTextinput label="Email" onGuiChange={(event) => save(event.detail.value)}/>;Angular
Section titled “Angular”Add CUSTOM_ELEMENTS_SCHEMA to the component that uses the tags, then bind properties and
events as usual:
@Component({ schemas: [CUSTOM_ELEMENTS_SCHEMA], template: ` <gui-textinput label="Email" [value]="email" (gui-change)="onChange($event)"></gui-textinput> `,})Tell Vue the gui-* tags are custom elements, for example in vite.config.ts:
vue({ template: { compilerOptions: { isCustomElement: (tag) => tag.startsWith('gui-') }, },});For typed props and @gui-* handlers in templates, add the typings once, for example in
env.d.ts:
import type {} from '@golemui/gui-components/vue';Use the tags in your templates, and bind arrays and objects as properties:
html`<gui-select label="Plan" .options=${plans} @gui-change=${onChange}></gui-select>`;Server-side rendering
Section titled “Server-side rendering”GolemUI Components load in Node without a DOM, so a framework that renders on the server can
import them. Out of the box the server sends the gui-* tags empty, and the browser fills them
in. To send their content in the server HTML too, install @lit-labs/ssr 4.1 or later and set up
your framework as below:
npm install @lit-labs/ssr@golemui/gui-components/ssr is the server-only entry point these setups use. How it works and
its limits are in Server rendering.
React SSR
Section titled “React SSR”Import the entry point once in your server entry. The React components then render their content on the server:
import { renderToString } from 'react-dom/server';import '@golemui/gui-components/ssr';Each component renders its own element, so streaming renders work too, such as
renderToPipeableStream. The components keep the server content until React has hydrated them.
Next.js
Section titled “Next.js”Use the React components. They are client components ("use client"), so they work in
the App Router. To render their content on the server, import the entry point in the root layout,
a Server Component:
import '@golemui/gui-components/ssr';import '@golemui/gui-components/index.css';Import it there rather than in instrumentation.ts, which doesn’t run when the build prerenders
static pages. The components then render their content in the App Router’s streaming render too.
Angular SSR
Section titled “Angular SSR”Set up Angular, and import the elements in src/main.server.ts as well as in
src/main.ts, so they render on the server. Then render them just before Angular serializes the
page, with a provider in your server config:
import { DOCUMENT, inject } from '@angular/core';import { BEFORE_APP_SERIALIZED } from '@angular/platform-server';import { renderElementsInDocument } from '@golemui/gui-components/ssr';
export const provideGolemuiServerRendering = () => ({ provide: BEFORE_APP_SERIALIZED, multi: true, useFactory: () => { const document = inject(DOCUMENT); return () => renderElementsInDocument(document); },});Add provideGolemuiServerRendering() to the providers next to provideServerRendering().
renderElementsInDocument reads the values bound as properties, such as [value] and
[options], as well as the attributes, so every binding reaches the server HTML.
Analog
Section titled “Analog”Analog renders its pages with Angular on the server. These steps start from a new Analog project.
Load the stylesheet in src/styles.css, which the starter links from index.html:
@import '@golemui/gui-components/index.css';Import the elements you use in one file:
import '@golemui/gui-components/textinput';import '@golemui/gui-components/select';Then import that file on both sides: in the browser, so the elements upgrade, and on the server, so they render.
import { bootstrapApplication } from '@angular/platform-browser';import { App } from './app/app';import { appConfig } from './app/app.config';import './elements';
bootstrapApplication(App, appConfig);import '@angular/platform-server/init';import { render } from '@analogjs/router/server';import { App } from './app/app';import { config } from './app/app.config.server';import './elements';
export default render(App, config);Add a provider to the server config that renders the elements just before Angular serializes the page:
import { type ApplicationConfig, DOCUMENT, inject, mergeApplicationConfig,} from '@angular/core';import { BEFORE_APP_SERIALIZED, provideServerRendering,} from '@angular/platform-server';import { renderElementsInDocument } from '@golemui/gui-components/ssr';import { appConfig } from './app.config';
const serverConfig: ApplicationConfig = { providers: [ provideServerRendering(), { provide: BEFORE_APP_SERIALIZED, multi: true, useFactory: () => { const document = inject(DOCUMENT); return () => renderElementsInDocument(document); }, }, ],};
export const config = mergeApplicationConfig(appConfig, serverConfig);Use the tags in your pages, with CUSTOM_ELEMENTS_SCHEMA:
import { Component, CUSTOM_ELEMENTS_SCHEMA, signal } from '@angular/core';
@Component({ schemas: [CUSTOM_ELEMENTS_SCHEMA], template: ` <gui-textinput name="email" label="Email" [value]="email()"></gui-textinput> <gui-select name="plan" label="Plan" [options]="plans" value="pro" ></gui-select> `,})export default class HomePage { email = signal('ada@example.com'); plans = [ { label: 'Free', value: 'free' }, { label: 'Pro', value: 'pro' }, ];}renderElementsInDocument reads the values bound as properties, such as [value] and
[options], so the server HTML has the email and both options.
Vue SSR
Section titled “Vue SSR”Set up Vue, then pass the HTML Vue rendered through renderElementsInHtml:
import { renderToString } from 'vue/server-renderer';import { renderElementsInHtml } from '@golemui/gui-components/ssr';import '@golemui/gui-components';
const html = renderElementsInHtml(await renderToString(app));renderElementsInHtml renders the empty elements in the HTML and leaves the rest of it byte for
byte as it was. It renders each element from its attributes, so a value bound as a property is
missing from the server HTML. See Limits.
Add the module to nuxt.config.ts:
export default defineNuxtConfig({ modules: ['@golemui/gui-components/nuxt'], css: ['@golemui/gui-components/index.css'],});It tells Vue the gui-* tags are custom elements and renders the elements of every page Nuxt
sends. Its options go under golemui:
| Option | Default | What it does |
|---|---|---|
serverRendering | true | Renders the elements’ content on the server. With false, Nuxt sends their tags empty. |
prefixes | ['gui-'] | The tag prefixes Vue compiles as custom elements. Add the prefix of a widget set of your own. |
With serverRendering: false, @lit-labs/ssr isn’t needed. Like Vue, the module renders each
element from its attributes. See Limits.
Lit SSR
Section titled “Lit SSR”Render a Lit template with renderTemplate:
import { html } from 'lit';import { renderTemplate } from '@golemui/gui-components/ssr';import '@golemui/gui-components/textinput';
const markup = await renderTemplate( html`<gui-textinput uid="email" name="email" label="Email"></gui-textinput>`,);The markup holds every element inert, with a defer-hydration attribute. In the browser, import
the elements and call resumeServerRendered():
import { resumeServerRendered } from '@golemui/gui-components';import '@golemui/gui-components/textinput';
resumeServerRendered();Pass the values as attributes. The browser builds the elements from the HTML, so a property
binding such as .options=${options} reaches the server render only.
Write the tags in your pages and register the elements with a client script:
---import '@golemui/gui-components/index.css';---
<gui-textinput label="Email" name="email"></gui-textinput>
<script> import '@golemui/gui-components/textinput';</script>Astro sends the tags as you wrote them. To render their content, add a middleware that passes
each HTML response through renderElementsInHtml:
import { defineMiddleware } from 'astro:middleware';import { renderElementsInHtml } from '@golemui/gui-components/ssr';import '@golemui/gui-components';
export const onRequest = defineMiddleware(async (_context, next) => { const response = await next(); if (!response.headers.get('content-type')?.startsWith('text/html')) return response; return new Response(renderElementsInHtml(await response.text()), response);});The middleware runs for the pages Astro renders on demand and for the pages it prerenders at build
time. Pass every value as an attribute, and arrays and objects as JSON, such as
options={JSON.stringify(plans)}.
TypeScript
Section titled “TypeScript”Each element exports its class and its events map (GuiTextinputEvents). GuiEventMap gives
the type of each event:
import type { GuiEventMap, GuiTextinputEvents } from '@golemui/gui-components';
type ChangeEvent = GuiEventMap<typeof GuiTextinputEvents>['gui-change'];