Getting started
Build your first generative UI webapp.
One component is all you need:
import { GeneratedUI } from 'getsyntux/client';
<GeneratedUI
endpoint="/api/syntux"
value={valueToDisplay}
hint="UI should look like..."
/>If you are passing a large array to value, use the skeletonize property to avoid high token costs.
See the following examples to understand its capabilities.
Examples
Basic example
Generate a simple UI with a hint:
import { GeneratedUI } from 'getsyntux/client';
export default function Page() {
const value = { username: 'John', email: 'john@gmail.com', age: 22 };
return (
<GeneratedUI
endpoint="/api/syntux"
value={value}
hint="display as a profile card"
/>
);
}import { GeneratedUI } from 'getsyntux/client';
import { useLoaderData } from '@remix-run/react';
export async function loader() {
return { username: 'John', email: 'john@gmail.com', age: 22 };
}
export default function Dashboard() {
const value = useLoaderData<typeof loader>();
return (
<GeneratedUI
endpoint="/api/syntux"
value={value}
hint="display as a profile card"
/>
);
}---
const value = { username: 'John', email: 'john@gmail.com', age: 22 };
---
<html>
<body>
<GeneratedUI
client:load
endpoint="/api/syntux"
value={value}
hint="display as a profile card"
/>
</body>
</html>Is the hint secret?
No. It is readable by the client.
Prompts you wish to be secret should be implemented at the endpoint.
Caching
Caching should be implemented on the controller side, then retrieved and passed to the client.
Use onGenerate to capture the schema.
In this example, we are using globalThis to store the cache in memory. This is only for demonstration! Use a real database in production.
const globalWithCache = globalThis as unknown as {
syntuxCache: Map<number, string> | undefined;
};
export const cache = globalWithCache.syntuxCache ?? new Map<number, string>(); // user id → schema
if(process.env.NODE_ENV !== 'production'){
globalWithCache.syntuxCache = cache;
}
export async function POST(request: Request){
const userId = 10;
const handler = createSyntuxHandler({
model: anthropic('claude-sonnet-4-5'),
spec,
onGenerate: (schema) => cache.set(userId, schema)
});
// custom logic here... (authentication)
return handler(request);
}const globalWithCache = globalThis as unknown as {
syntuxCache: Map<number, string> | undefined;
};
export const cache = globalWithCache.syntuxCache ?? new Map<number, string>(); // user id → schema
if(process.env.NODE_ENV !== 'production'){
globalWithCache.syntuxCache = cache;
}
export async function action({ request }: ActionFunctionArgs) {
const userId = 10;
const handler = createSyntuxHandler({
model: anthropic('claude-sonnet-4-5'),
spec,
onGenerate: (schema) => cache.set(userId, schema)
});
// custom logic here... (authentication)
return handler(request);
}const globalWithCache = globalThis as unknown as {
syntuxCache: Map<number, string> | undefined;
};
export const cache = globalWithCache.syntuxCache ?? new Map<number, string>(); // user id → schema
if(process.env.NODE_ENV !== 'production'){
globalWithCache.syntuxCache = cache;
}
export const POST: APIRoute = async ({ request }) => {
const userId = 10;
const handler = createSyntuxHandler({
model: anthropic('claude-sonnet-4-5'),
spec,
onGenerate: (schema) => cache.set(userId, schema)
});
// custom logic here... (authentication)
return handler(request);
}Then, provide the cached value to the cached property:
import { GeneratedUI } from 'getsyntux/client';
import { cache } from './api/syntux/route';
export default function Page() {
const userId = 10;
const value = { username: 'John', email: 'john@gmail.com', age: 22 };
return (
<GeneratedUI
endpoint="/api/syntux"
value={value}
hint="UI should look like..."
cached={cache.get(userId)}
/>
);
}import { GeneratedUI } from 'getsyntux/client';
import { useLoaderData } from '@remix-run/react';
import { cache } from './api.syntux';
export async function loader() {
const userId = 10;
return {
cached: cache.get(userId),
value: { username: 'John', email: 'john@gmail.com', age: 22 },
};
}
export default function Dashboard() {
const { cached, value } = useLoaderData<typeof loader>();
return (
<GeneratedUI
endpoint="/api/syntux"
value={value}
hint="UI should look like..."
cached={cached}
/>
);
}---
import { Dashboard } from '../components/Dashboard';
import { cache } from './api/syntux';
const userId = 10;
const value = { username: 'John', email: 'john@gmail.com', age: 22 };
---
<GeneratedUI
client:load
endpoint="/api/syntux"
value={value}
hint="UI should look like..."
cached={cache.get(userId)}
/>Custom components
Use custom React components, either your own or somebody else's (a UI library):
import { GeneratedUI } from 'getsyntux/client';
import { Card, Avatar } from '@/components/ui';
export default function Page() {
const value = { username: 'John', email: 'john@gmail.com', avatar: '/john.png' };
return (
<GeneratedUI
endpoint="/api/syntux"
value={value}
hint="use custom components when possible"
components={[
{
name: 'Card',
props: '{ title: string, body: string }',
component: Card,
},
{
name: 'Avatar',
props: '{ src: string, alt: string }',
component: Avatar,
context: 'Displays a circular profile image.', /* optional */
},
]}
/>
);
}import { GeneratedUI } from 'getsyntux/client';
import { Card, Avatar } from '~/components/ui';
import { useLoaderData } from '@remix-run/react';
export default function Dashboard() {
const value = useLoaderData<typeof loader>();
return (
<GeneratedUI
endpoint="/api/syntux"
value={value}
hint="use custom components when possible"
components={[
{ name: 'Card', props: '{ title: string, body: string }', component: Card },
{ name: 'Avatar', props: '{ src: string, alt: string }', component: Avatar },
]}
/>
);
}---
const value = { username: 'John', email: 'john@gmail.com', avatar: '/john.png' };
---
<GeneratedUI
client:load
endpoint="/api/syntux"
value={value}
hint="use custom components when possible"
components={[
{ name: 'Card', props: '{ title: string, body: string }', component: Card },
{ name: 'Avatar', props: '{ src: string, alt: string }', component: Avatar },
]}
/>Make sure components are marked with "use client".
The components array can be automatically generated using the generate-defs CLI command.
Customize animation
By default, new elements fade in from below when mounted.
This motion cannot yet be customized. However, the duration and offset can, using the animate property:
<GeneratedUI
model={anthropic("claude-sonnet-4-5")}
value={valueToDisplay}
animate={{
offset: 10, // pixels
duration: 100 // ms
}}
/>In order to disable the animation, set offset to 0 and/or duration to 0.
Animating custom components
By default, syntux is conservative when applying animations in order to not break the tree layout.
Animations are applied through the style property, via the opacity, transform, transition and willChange attributes.
The animation may not work for a custom component, but this can be fixed!
If the animation isn't displaying, that probably means your component doesn't respond to the style property. You need to include style as a property.
For instance:
export function CustomComponent({ ...rest }){ /* <-- rest takes care of style */
return <div {...rest}>
{/* original code here */}
</div>
}