Loading the elements
There are three ways to get the kai-* elements onto a page. They render the same components — the difference is how their code reaches the browser. Most apps want the first one.
Quick comparison
Section titled “Quick comparison”| Strategy | Import | Reach for it when |
|---|---|---|
| Register all | @kitn.ai/ui/elements | The default. You use several elements, or you just want it to work. Works in SSR. |
| Per-element | @kitn.ai/ui/elements/chat | You use only a couple of elements and your build tree-shakes. |
| Autoloader | <script> (CDN / static) | No build step — a CDN or static page. Loads each element on demand. |
Register all (default)
Section titled “Register all (default)”import '@kitn.ai/ui/elements';One import, every kai-* element registered. The simplest setup, and the SSR-safe entry point — the import is side-effect-only and doesn’t touch document until the browser runs it, so it won’t throw in a server render.
Per-element imports
Section titled “Per-element imports”Import an element by its file basename, and your bundler includes only that element’s code:
import '@kitn.ai/ui/elements/chat';import '@kitn.ai/ui/elements/code-block';The import path is the element’s filename in dist/elements/ — usually the tag minus the kai- prefix. You only need a row below for an element you place in your markup yourself; the message / markdown / reasoning / input parts of a chat are already inside kai-chat:
| Element | Import |
|---|---|
<kai-chat> | @kitn.ai/ui/elements/chat |
<kai-code-block> | @kitn.ai/ui/elements/code-block |
<kai-message> | @kitn.ai/ui/elements/message |
<kai-prompt-input> | @kitn.ai/ui/elements/prompt-input |
<kai-markdown> | @kitn.ai/ui/elements/markdown |
<kai-reasoning> | @kitn.ai/ui/elements/reasoning |
<kai-attachments> | @kitn.ai/ui/elements/attachments |
<kai-artifact> | @kitn.ai/ui/elements/artifact |
Autoloader
Section titled “Autoloader”The autoloader watches the DOM and registers each element on demand — only the tags present on a given page are ever loaded.
It is a CDN / static-file tool: load it from a <script type="module"> tag. It is not importable through a bundler — Vite and webpack relocate it away from the element files and its on-demand imports 404. In a bundled app, use per-element imports instead.
<script type="module" src="https://cdn.jsdelivr.net/npm/@kitn.ai/ui/dist/elements/autoloader.js"></script>Here it is running live. This iframe loads only the autoloader — watch the panel for the modules it fetches on demand, then add an element and see it load on the fly:
On import it scans for undefined kai-* elements and starts a MutationObserver. Any kai-* element added to the DOM later — including ones injected by client-side routing — is registered the moment it appears.
<!-- Only kai-chat loads. The others register only if you add them. --><kai-chat style="display:block; height:100dvh;"></kai-chat>
<script type="module"> import 'https://cdn.jsdelivr.net/npm/@kitn.ai/ui/dist/elements/autoloader.js';
const chat = document.querySelector('kai-chat'); chat.messages = [{ id: '1', role: 'assistant', content: 'Hello!' }]; chat.addEventListener('kai-submit', (e) => console.log(e.detail.value));</script>Code highlighting
Section titled “Code highlighting”Syntax highlighting (Shiki) loads on demand — per language, the first time a code block with that language renders, with no WebAssembly. If you never render a code block, it never loads. This holds for all three strategies above.
SSR notes
Section titled “SSR notes”| Strategy | SSR-safe? |
|---|---|
@kitn.ai/ui/elements | Yes — side-effect only; no document on import |
Per-element (@kitn.ai/ui/elements/chat) | No — use inside a client-only boundary |
| Autoloader | No — uses document; CDN <script>, not for SSR |
For SSR meta-frameworks, see the meta-frameworks guide for framework-specific client boundary patterns.