---
title: Introduction
---

# Your views in JSX. Your app in HTML.

Vincle lets you compose typed JSX views in your existing server.
Start with one component, render it to HTML, and return it from a route.

## Compose a view

With `@vincle/core`, components are functions, props are TypeScript values, and
the result is an HTML string. The renderer adds no browser runtime.

```tsx
import { renderToString } from "@vincle/core";

function App({ name }: { name: string }) {
  return <h1>Hello, {name}!</h1>;
}

const page = <App name="world" />;
const html = await renderToString(page);
```

```html
<h1>Hello, world!</h1>
```

Set up [the package](/guide/getting-started/installation) and
[JSX in TypeScript](/guide/getting-started/typescript-setup), then connect
the rendered HTML to your server's response API. You keep your routing,
data access, and deployment setup.

## Load data in your components

A component can be async. The renderer awaits its result before producing
the final HTML.

```tsx
import { renderToString } from "@vincle/core";

declare const db: {
  users: {
    findById(id: string): Promise<{ name: string; email: string }>;
  };
};

async function UserCard({ id }: { id: string }) {
  const user = await db.users.findById(id);
  return (
    <div class="card">
      <h2>{user.name}</h2>
      <p>{user.email}</p>
    </div>
  );
}

const html = await renderToString(<UserCard id="42" />);
```

Components execute in document order, one at a time, and the string response
waits for the whole tree.

## Let slower parts arrive later

Add `@vincle/flow` when a page should appear before all its data is ready.
Wrap a slow component in `Defer`, provide a fallback, and use the streaming
renderer to send the surrounding page before its deferred fragments. An
[adapter](/integration/adapters) decides how fragments update the page: natively,
or through HTMX or Turbo.

```tsx
import { renderToStream, Defer } from "@vincle/flow";
import { NativeAdapter } from "@vincle/flow/adapters";

const stream = renderToStream(
  () => (
    <html>
      <body>
        <h1>Page visible immediately</h1>
        <Defer target="comments" fallback={<p>Loading comments…</p>}>
          <Comments />
        </Defer>
      </body>
    </html>
  ),
  NativeAdapter,
);
// → ReadableStream<string>. Pipe it to the HTTP response.
```

[Follow a view from a route to streaming HTML](/guide/views).

## What the core gives you

You get an HTML string. Here's what makes that worth it:

- **TypeScript checks your templates.** Props, attributes, CSS values — all typed.
  Misspell `class` as `cls`, and the compiler catches it before you run anything.
  See the [Context & Scope API](/api/core/scope).

- **Security is the default, not a config.** Every value is escaped for its context.
  URL attributes only accept safe schemes. `raw()` is the explicit opt-out — if HTML
  escapes you, it's one function call. See the [security model](/guide/security).

- **The output works immediately.** A complete HTML string you can send as a response,
  write to disk for SSG, or pass to an email provider. No transformation step.
  Email client compatibility is still your template design problem, not the renderer's.

Think of it as a faster, simpler alternative to React's server rendering —
but without the client-side runtime.

Vincle does not send a JavaScript runtime to the browser. Components run on the
server and produce HTML. Add client-side behavior with the tools of your choice.

## Next steps

- [Installation](/guide/getting-started/installation): install and configure JSX
- [First render](/guide/getting-started/first-render): write a component
- [From a view to progressive HTML](/guide/views): integrate a route and defer results
- [Elements & attributes](/guide/jsx/elements-attributes): JSX conventions
