Skip to content
Vincle

Loading…

    First render

    Everything you need to render your first JSX to an HTML string.

    Hello world#

    renderToString always returns a Promise<string>, even for synchronous trees. The string is ready to send over HTTP or write to a file.

    import { renderToString } from "@vincle/core";
    const html = await renderToString(
    <html lang="en">
    <head>
    <title>Hello, world!</title>
    </head>
    <body>
    <h1>Hello, world!</h1>
    </body>
    </html>,
    );
    <html lang="en">
    <head>
    <title>Hello, world!</title>
    </head>
    <body>
    <h1>Hello, world!</h1>
    </body>
    </html>

    Components are functions#

    A component is a plain function: props in, JSX out. No classes, no hooks, no lifecycle. Children arrive in the children prop.

    import { renderToString, type JSX } from "@vincle/core";
    function Layout({ title, children }: { title: string; children: JSX.Element | JSX.Element[] }) {
    return (
    <html lang="en">
    <head>
    <title>{title}</title>
    </head>
    <body>{children}</body>
    </html>
    );
    }
    const html = await renderToString(
    <Layout title="My page">
    <h1>Welcome</h1>
    <p>This is a page rendered with Vincle.</p>
    </Layout>,
    );

    Same props, same output: components are easy to test and cache.

    Values are escaped#

    Every value is escaped for where it lands. raw() is the only way to emit markup, for HTML you trust.

    import { renderToString, raw } from "@vincle/core";
    // User input is HTML-escaped automatically
    const userInput = '<script>alert("xss")</script>';
    const html = await renderToString(<p>{userInput}</p>);
    // Use raw() only for trusted HTML you generated yourself
    const trustedHtml = "<em>rendered from your own markdown</em>";
    const html2 = await renderToString(<article>{raw(trustedHtml)}</article>);
    <p>&lt;script&gt;alert("xss")&lt;/script&gt;</p>
    <article><em>rendered from your own markdown</em></article>

    See the security model.

    Async works directly#

    Any component can be an async function. renderToString awaits the whole tree, so the component that needs data is the component that fetches it.

    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" />);

    Where to next#