---
url: /guide/introduction.md
---
You are viewing documentation for the upcoming [**v5**](https://github.com/kitajs/html/tree/next) release. For the current stable version, see the [v4 readme on npm](https://www.npmjs.com/package/@kitajs/html).# What is Kita Html
Kita Html is a JSX runtime where every element evaluates to a string. Where React's
`
` produces a virtual DOM node that must be reconciled and serialized, Kita Html's
`` returns `''` directly. There is no intermediate representation, no
diffing step, and no serialization pass. The JSX you write compiles to function calls that
concatenate strings.
This architecture makes Kita Html significantly faster than virtual DOM-based renderers
for any scenario where the output is an HTML string: server-side rendering, static site
generation, email templates, HTMX applications, or any HTTP handler that sends HTML over
the wire.
[Go to getting started](/guide/getting-started.md)
## Performance through simplicity
A virtual DOM renderer performs three operations per render cycle: it constructs an object
tree, diffs it against the previous tree, and serializes the result to a string. Kita Html
skips all three. The TypeScript compiler rewrites JSX expressions into `jsx()` and
`jsxs()` function calls at build time. At runtime, those functions concatenate attribute
strings and child strings into a single result. The output is the final HTML with no
post-processing.
```tsx twoslash kita
import { jsx as __jsx } from '@kitajs/html/jsx-runtime'
const username: string = 'Username'
export let html: JSX.Element
// ---cut---
// What you write
html = (
{username}
)
// What TypeScript compiles to
html = __jsx('div', {
class: 'card',
children: __jsx('h1', {
safe: true,
children: username
})
})
// Click on the <> icon in the top right to see the compiled output
```
This directness is why Kita Html benchmarks 7-40x faster than React's `renderToString`
across real-world component trees. The runtime has zero dependencies beyond TypeScript
itself.
[Read more about how JSX becomes HTML](/guide/how-jsx-becomes-html.md)
## XSS protection
Because `JSX.Element` is a `string`, the runtime cannot distinguish between HTML produced
by a component and raw user input injected as a child. Children are not escaped by
default. This is a deliberate trade-off for performance and composability, and it is fully
addressed by three layers of protection that work together to make accidental XSS
practically impossible.
The `safe` attribute escapes children at render time. Adding it to any native element
causes the runtime to pass all children through HTML entity escaping before concatenation.
```tsx twoslash kita
// @noErrors
let userInput = `` as const
// ---cut---
const html = {userInput}
// ^?
```
A TypeScript language service plugin analyzes every JSX expression in your editor and
flags any child whose type could carry unescaped HTML. The same analysis runs as a CLI
tool [`xss-scan`](/guide/xss/scanner-cli.md) in CI/CD pipelines, failing the build on any
finding. Between the editor diagnostics and the CI gate, unsafe expressions are caught
before they reach production.
**The only way to ship XSS-vulnerable code is to explicitly suppress the warnings.**
[Read more about the XSS protection system](/guide/xss/safe-attribute.md)
## Async components and Suspense
Kita Html supports async components natively. Any function component that returns a
`Promise` is valid JSX. When an async child appears anywhere in the tree, the
entire parent chain becomes a `Promise` through automatic propagation.
```tsx twoslash kita
const db = {
async getUser(id: string) {
return { name: 'Arthur' }
}
}
// ---cut---
async function UserCard({ id }: { id: string }) {
const user = await db.getUser(id)
return {user.name}
}
// The result is a Promise because UserCard is async,
// Type still is JSX.Element so it can be used in JSX
const html =
```
For streaming scenarios, the Suspense component renders a fallback immediately and
replaces it with the resolved content as each async subtree completes. This uses HTTP
chunked transfer encoding to stream updates to the client without waiting for the entire
page to resolve. A small inline script handles the client-side DOM replacement.
```tsx twoslash kita=stream
import { Suspense, renderToStream } from '@kitajs/html/suspense'
async function UserCard({ id }: { id: string }) {
return Async Component
}
// ---cut---
const stream = renderToStream((rid) => (
Loading...}>
))
// Pipe the stream to your HTTP response or use one of our integrations.
```
Each Suspense boundary operates independently, so fast components appear instantly while
slow ones show their fallback. The `rid` parameter ties each Suspense instance to a
specific request for safe concurrent rendering.
- [Read more about Suspense streaming](/guide/async/async-components.md)
## Packages
As of now, Kita Html is composed of two main packages:
- `@kitajs/html` is the core runtime that compiles JSX to strings and provides Suspense
streaming.
- `@kitajs/ts-html-plugin` is the TypeScript plugin and CLI scanner for XSS detection.
Official [framework integrations](/integrations/overview.md) are available for Fastify,
Elysia, and others. The core runtime works with any framework that accepts strings.
- [Read more about the packages and integrations](/integrations/overview.md)
---
url: /guide/getting-started.md
---
You are viewing documentation for the upcoming [**v5**](https://github.com/kitajs/html/tree/next) release. For the current stable version, see the [v4 readme on npm](https://www.npmjs.com/package/@kitajs/html).# Getting Started
Install `@kitajs/html` and `@kitajs/ts-html-plugin` together. The first is the JSX
runtime; the second provides editor diagnostics and the `xss-scan` CLI for catching XSS at
compile time.
```sh [npm]
npm i @kitajs/html@next @kitajs/ts-html-plugin@next
```
```sh [yarn]
yarn add @kitajs/html@next @kitajs/ts-html-plugin@next
```
```sh [pnpm]
pnpm add @kitajs/html@next @kitajs/ts-html-plugin@next
```
```sh [bun]
bun add @kitajs/html@next @kitajs/ts-html-plugin@next
```
## TypeScript configuration
Add the following to your `tsconfig.json`. The `jsx` and `jsxImportSource` fields tell
TypeScript to compile JSX using the Kita Html runtime. The `plugins` entry activates the
XSS detection plugin in your editor.
```json title="tsconfig.json"
{
"compilerOptions": {
"jsx": "react-jsx",
"jsxImportSource": "@kitajs/html",
"plugins": [{ "name": "@kitajs/ts-html-plugin" }]
}
}
```
If your editor uses a globally installed TypeScript version instead of the project's local
copy, the plugin will not load. In VS Code, configure the workspace to use the local
TypeScript:
```json title=".vscode/settings.json"
{
"typescript.tsdk": "node_modules/typescript/lib",
"typescript.enablePromptUseWorkspaceTsdk": true
}
```
## XSS scanner
Add `xss-scan` to your test script so it runs in CI alongside your tests. The CLI performs
the same XSS analysis as the editor plugin, but against your entire codebase.
```json title="package.json"
{
"scripts": {
"test": "xss-scan && vitest"
}
}
```
## Verification
Write the following in a `.tsx` file:
```tsx twoslash
// @errors: 88601
console.log({String.name}
)
```
Your editor should show an XSS error on `String.name`, because its type is `string` and it
is not escaped. If you see the error, the plugin is working. Run `xss-scan` from the
terminal to confirm the CLI catches the same issue.
If no error appears, verify that your editor is using the workspace TypeScript version and
that the `plugins` array is present in the `tsconfig.json` that covers the file.
---
url: /guide/how-jsx-becomes-html.md
---
You are viewing documentation for the upcoming [**v5**](https://github.com/kitajs/html/tree/next) release. For the current stable version, see the [v4 readme on npm](https://www.npmjs.com/package/@kitajs/html).# How JSX Becomes HTML
The transformation from JSX to an HTML string happens in two stages: a compile-time
rewrite by TypeScript and a runtime evaluation by the Kita Html functions.
## Compile time
When `tsconfig.json` specifies `jsx: "react-jsx"` and `jsxImportSource: "@kitajs/html"`,
TypeScript rewrites every JSX expression into a function call. Elements with a single
child call `jsx()`. Elements with multiple children call `jsxs()`. Attributes and children
are passed as a single props object.
```tsx title="Source TSX" twoslash kita
const html = (
{[1, 2].map((i) => (
- {i}
))}
)
```
TypeScript compiles this to:
```tsx title="Compiled output" twoslash kita
// @showEmit
const html = (
{[1, 2].map((i) => (
- {i}
))}
)
```
The JSX syntax is fully removed at compile time. The runtime never parses JSX.
## Runtime
The `jsx()` and `jsxs()` functions receive the tag name and the props object. They perform
three operations in sequence: serialize attributes into an HTML attribute string,
serialize children into a content string, and concatenate the opening tag, content, and
closing tag.
If any child is a `Promise`, the concatenation returns a `Promise` instead of a
`string`. This propagation is automatic: a single async component anywhere in the tree
makes every ancestor async up to the root.
Void elements like `
`, `
`, and `` skip the closing tag entirely. The
runtime checks the tag name against a list ordered by frequency so the most common void
elements resolve in a single comparison.
Attributes are always escaped. The `class` attribute accepts arrays for conditional
composition. The `style` attribute accepts both strings and objects, with automatic
camelCase-to-kebab-case conversion for object keys. Boolean attributes like `disabled`
render as valueless attributes when `true` and are omitted when `false`.
---
url: /guide/ai-support.md
---
You are viewing documentation for the upcoming [**v5**](https://github.com/kitajs/html/tree/next) release. For the current stable version, see the [v4 readme on npm](https://www.npmjs.com/package/@kitajs/html).# AI Support
Kita Html exposes multiple AI-friendly entry points so coding agents can understand the
runtime without falling back to React assumptions. Use the installable skill when you want
behavior guidance inside an agent, then use the published docs formats to provide focused
or broad reference material.
## Agent skill
The `kita-html` skill is designed for coding agents that support installable skills. It
teaches the core runtime model, XSS rules, framework adapters, async components, and
Suspense streaming so the agent stops treating every `.tsx` file as React.
Install it with:
```sh [npx]
npx skills add kitajs/html --skill kita-html
```
```sh [yarn]
yarn dlx skills add kitajs/html --skill kita-html
```
```sh [pnpm]
pnpm dlx skills add kitajs/html --skill kita-html
```
```sh [bunx]
bunx skills add kitajs/html --skill kita-html
```
```sh [deno]
deno run -A npm:skills add kitajs/html --skill kita-html
```
After installation, prompt the agent normally. Useful examples:
```text
Build this Fastify page with Kita Html and stream async sections.
```
```text
Fix this Kita TSX so user bios render safely and xss-scan passes.
```
```text
Convert this React-style server component into Kita Html.
```
## Documentation formats
Kita Html publishes the docs in three useful formats.
- [`llms.txt`](https://html.kitajs.org/llms.txt) is the lightweight index. Use it when AI
should pick the right page first.
- [`llms-full.txt`](https://html.kitajs.org/llms-full.txt) is the full concatenated
documentation dump. Use it when AI needs broad Kita Html context in a single fetch.
- Every page also has a raw Markdown version, such as
[Getting Started.md](https://html.kitajs.org/guide/getting-started.md) or
[Safe Attribute.md](https://html.kitajs.org/guide/xss/safe-attribute.md).
Choose the smallest format that matches the task. `llms.txt` is usually best for
discovery. Raw Markdown is usually best for one topic. `llms-full.txt` is best when the
agent needs a full view of the framework.
## Project instructions
If your editor or coding agent supports project instruction files, point it at Kita Html
before it writes JSX.
```markdown title="AGENTS.md"
# AGENTS.md
This project uses Kita Html, not React.
## Docs
- Kita Html index: https://html.kitajs.org/llms.txt
- Kita Html full docs: https://html.kitajs.org/llms-full.txt
- Getting started: https://html.kitajs.org/guide/getting-started.md
- Safe attribute: https://html.kitajs.org/guide/xss/safe-attribute.md
## Rules
- `JSX.Element` is `string | Promise`.
- Children are not escaped by default.
- Use `safe` on native elements that render untrusted text.
- Run `xss-scan` during validation.
```
If your tool also supports a file like `CLAUDE.md`, reference the same instructions there.
The important part is that the agent sees Kita-specific rules before it starts generating
TSX.
## What To Share With AI
Share the narrowest document that answers the task.
- For setup issues, share
[Getting Started.md](https://html.kitajs.org/guide/getting-started.md).
- For escaping questions, share
[Safe Attribute.md](https://html.kitajs.org/guide/xss/safe-attribute.md) or
[Safety Rules.md](https://html.kitajs.org/guide/xss/safety-rules.md).
- For streaming and async behavior, share
[Using Suspense.md](https://html.kitajs.org/guide/async/using-suspense.md).
- For framework integration, share the matching Fastify, Express, or Elysia guide.
- For broader assistance, start with [`llms.txt`](https://html.kitajs.org/llms.txt) and
move to [`llms-full.txt`](https://html.kitajs.org/llms-full.txt) only when the task
needs wider context.
---
url: /guide/xss/safe-attribute.md
---
You are viewing documentation for the upcoming [**v5**](https://github.com/kitajs/html/tree/next) release. For the current stable version, see the [v4 readme on npm](https://www.npmjs.com/package/@kitajs/html).# Using the Safe Attribute
Every child expression in Kita Html renders without escaping by default. To escape user
input, apply the `safe` attribute to the nearest native element wrapping the expression.
## Native elements
Add `safe` to the element that directly contains the dynamic content. The runtime passes
all children through HTML entity escaping before concatenation.
```tsx twoslash kita
// @noErrors
let userInput = `` as const
// ---cut---
const html = {userInput}
// ^?
```
Without `safe`, malicious input executes. Consider a user profile where the description
field contains
``.
Rendering this without escaping closes the container early, injects a script tag, and runs
arbitrary code. The `safe` attribute converts the angle brackets to `<` and `>`,
rendering the payload as harmless text.
Place `safe` on the innermost element that holds the untrusted value. Do not add it to a
parent wrapper, as that would escape the HTML of child components too.
```tsx twoslash kita
function UserCard({ name, bio }: { name: string; bio: string }) {
return (
)
}
// ---cut-after---
const html =
```
## Component children
The `safe` attribute only applies to native elements. If you pass an unsafe value as a
child to a component, you must either escape it manually or wrap it in a Fragment with the
`safe` attribute to tell the plugin it's already escaped. This prevents components from
accidentally rendering unescaped HTML when they receive dynamic content as children.
```tsx twoslash kita
let userInput: string = 'User input!'
import { Fragment, Children, escapeHtml } from '@kitajs/html'
function MyComponent({ children }: { children: Children }) {
return {children}
}
let html: JSX.Element
// ---cut---
html = (
{userInput}
)
// Or escape explicitly when the component doesn't support safe
html = {escapeHtml(userInput)}
```
## Template literal helper
The `e` tagged template is an alias to `escapeHtml()` that you can use as interpolated
content in template literals. It provides a convenient way to escape dynamic values
outside of JSX.
```tsx twoslash kita
const userName = '' as const
// ---cut---
import { e } from '@kitajs/html'
const html = e`Hello, ${userName}!`
```
## Suppression conventions
When the XSS detection plugin flags a value that you know is safe, you can suppress the
warning without adding `safe` to the element, which would escape all children and
potentially break components.
::: danger
Suppressing warnings is dangerous because it allows unescaped HTML to slip through. Only
use these techniques when you have a guarantee of safety.
:::
Prefix the variable name with `safe`. The plugin treats any identifier starting with
`safe` as pre-escaped.
```tsx twoslash kita
const rawInput = "" as const
function sanitizeElsewhere(raw: string): string {
// Some external library or custom logic that guarantees safety
return raw.replace(//g, '>')
}
// ---cut---
const safeContent = sanitizeElsewhere(rawInput)
// ^?
const html = {safeContent}
// ^?
```
Following the same logic, prefixing a variable with `unsafe` explicitly marks it as
unescaped and triggers a warning if used in JSX.
```tsx twoslash kita
// @errors: 88601
const unsafeContent = 'My safe string' as const
const html = {unsafeContent}
```
::: warning
Manual casts and naming conventions are not enforced by the compiler. They rely on
developer discipline and code reviews to ensure safety. Use them judiciously and document
the rationale for any exceptions.
:::
Cast the expression to `'safe'` inline. This tells the plugin to skip the check for that
specific usage and not warn about possible XSS vulnerabilities.
```tsx twoslash kita
const content: string = ''
// ---cut---
const html = {content as 'safe'}
```
Call `Html.escapeHtml()` directly. The plugin recognizes the return value as escaped.
```tsx twoslash kita
const content: string = ''
// ---cut---
import { Html } from '@kitajs/html'
const html = {Html.escapeHtml(content)}
```
---
url: /guide/xss/scanner-cli.md
---
You are viewing documentation for the upcoming [**v5**](https://github.com/kitajs/html/tree/next) release. For the current stable version, see the [v4 readme on npm](https://www.npmjs.com/package/@kitajs/html).# Running the XSS Scanner
The `xss-scan` CLI ships with `@kitajs/ts-html-plugin` and performs project-wide XSS
analysis from the command line.
## Usage
```bash
xss-scan [options] [files...]
```
The command can also be invoked as `ts-html-plugin`, which is an alias for the same CLI.
When called without file arguments, it scans all files included by the project's
`tsconfig.json`. When file paths are provided, only those files are analyzed.
```bash
# Scan entire project
xss-scan
# Scan specific files
xss-scan src/pages/login.tsx src/pages/profile.tsx
```
## Options
`--cwd ` sets the working directory. Defaults to the current directory.
`-p, --project ` specifies the path to `tsconfig.json`. Defaults to `tsconfig.json`
in the working directory.
`-s, --simplified` outputs diagnostics in a compact single-line format, useful for machine
parsing.
## Exit codes
The scanner exits with code 0 when no issues are found, 1 when errors are present, and 2
when only warnings exist. This makes it suitable for CI gates where you want to fail on
errors but optionally allow warnings.
## Integration
Add the scanner to your test script so it runs before or alongside your test suite.
```json
{
"scripts": {
"test": "xss-scan && vitest"
}
}
```
In a GitHub Actions workflow:
```yaml
- name: XSS scan
run: npx xss-scan
```
The scanner uses the same TypeScript program and analysis engine as the editor plugin, so
findings are identical between the two.
---
url: /guide/xss/safety-rules.md
---
You are viewing documentation for the upcoming [**v5**](https://github.com/kitajs/html/tree/next) release. For the current stable version, see the [v4 readme on npm](https://www.npmjs.com/package/@kitajs/html).# Safety Rules
The XSS detection engine classifies every JSX child expression as safe or unsafe based on
its TypeScript type. This page lists the rules the engine follows.
## Safe types
Expressions with these types render without escaping and produce no diagnostic:
- `number`, `boolean`, `bigint`, `null`, `undefined`
- String literals (e.g. `'hello'` rather than `string`)
- `JSX.Element` (the output of another component, already rendered)
- `Html.Children` (the typed children alias)
- Return values of `Html.escapeHtml()`, `Html.escape()`, or `e` template literals
- Variables whose name starts with `safe` (e.g. `safeContent`, `safeHtml`)
## Unsafe types
Expressions with these types trigger a diagnostic unless wrapped with `safe` or escaped
manually:
- `string` (a dynamic string that could contain HTML)
- `any` (the engine cannot verify safety without a concrete type)
- Objects relying on `toString()` for rendering
## Composite types
Union types are safe only if every member of the union is safe. `string | number` is
unsafe because `string` is unsafe. `number | boolean` is safe because both members are
safe.
Both branches of ternary and binary expressions are checked independently. Even if one
branch is unreachable at runtime, the engine still analyzes its type.
```tsx twoslash
// @errors: 88601
const condition = true
const safeValue = 42
const unsafeString = 'user input'
;{condition ? safeValue : unsafeString}
```
## Exceptions
Content inside `
```
The `` is inert, the browser does not render its content. The `