Components
Markdown
Streaming-safe GFM: unterminated syntax closed while streaming, no raw HTML, images only from hosts you allow.
Example
The limiter belongs at the top of the handler, before streamText opens the stream:
- Read the client's IP from
x-forwarded-for. - Call
ratelimit.limit(ip)and return a 429 when it fails.
| Window | Requests | Burst |
|---|---|---|
| Sliding, 10 s | 10 | smoothed |
| Fixed, 10 s | 10 | up to 20 at the edge |
const { success } = await limit(ip);if (!success) return tooMany();Interactive, and the same on the components page. Try the keyboard below on it.
Installation
npm i @dgesteves/agent-ui-kit aiimport { Markdown } from '@dgesteves/agent-ui-kit';
// Once per app (with Tailwind v4: @import '@dgesteves/agent-ui-kit/tailwind.css'; in your CSS)
import '@dgesteves/agent-ui-kit/styles.css';npx shadcn@latest add @agent-ui-kit/markdownimport { Markdown } from '@/components/agent-ui/markdown';The styles are once per app; Getting started has the Tailwind v4 and plain CSS options.
Usage
<Markdown streaming={part.state === 'streaming'} citations={sources.length}>
{part.text}
</Markdown>API reference
Generated from the types the package ships, so it matches the version you install.
Markdown
Streaming-safe markdown (GFM). While streaming, unterminated emphasis, code
and links are closed before parsing so partial output never flashes raw syntax.
Raw HTML is not rendered, links and images with unsafe protocols (javascript:,
data:) are stripped, and images load only from allowedImageHosts.
Props
childrenrequiredstringstreamingbooleanDefaultfalseRepairs unterminated syntax and shows a caret while text is still arriving.
citationsnumberDefault0Number of available sources;
[n]markers up to this count become citation links.citationPrefixstringDefault'source'Must match the
idPrefixof theSourcesthe citations point to.allowedImageHostsreadonly string[]Where images may load from. Default: nowhere. The browser fetches an image as soon as it renders, so a URL in model output can leak data (
); a blocked image renders as a link with its alt text, and nothing is requested.- A host name of http(s) URLs, matched exactly:
'images.example.com'(any port), or'localhost:3000'(that port only). 'self': relative URLs (/logo.png,./chart.png), which load from your own origin.//host/xis not relative. An absolute URL to your own site needs its host listed.'*': every image.
Compared by value, so an inline array does not re-render the markdown.
- A host name of http(s) URLs, matched exactly:
componentsComponentsElement overrides, merged over the defaults. Keep the object stable: a new one re-renders the markdown.
classNamestring
Accessibility
- Raw HTML in model output is never rendered, and links and images with
javascript:ordata:URLs are stripped. - Images load only from
allowedImageHosts. Others render as a link with their alt text, read as "Image:", and nothing is requested. [n]markers up tocitationsbecome links named "Source n".- The streaming caret is hidden from screen readers.
Theming
Every color, radius and font is a CSS variable, so the overrides in Theming apply, on :root or scoped to any element. These hooks are read from the component's markup.
- Slots (data-slot)
- markdown
- Tokens it uses
- --aui-accent--aui-accent-fg--aui-bg--aui-border--aui-border-strong--aui-fg--aui-fg-muted--aui-fg-subtle--aui-font-mono--aui-font-sans--aui-ring--aui-surface-2
/* Only this component, and only inside .settings-panel */
.settings-panel [data-slot='markdown'] {
--aui-radius: 4px;
}