TanStack Start Quickstart
Create a new Flexkit Studio in a TanStack Start application and run it locally.
Before you start
Use Node.js 22 or later and a package manager. Create a Flexkit project (the Start from scratch option fits best), copy its Project ID from the Dashboard, and ensure your account can deploy its schema. New projects let the agent manage the schema, which refuses flexkit deploy; an owner switches the project to Managed from code under Settings → General → Schema Management first. These examples use a project you can safely populate with synthetic data.
Keep the generated application’s package versions locked. See compatibility when upgrading. Use the directory layout shown here; adjust relative imports if you choose a src directory in Next.js.
Create a new TanStack Start project
pnpm
pnpm create @tanstack/start@latest my-flexkit-app --package-manager pnpm
cd my-flexkit-appnpm
npm create @tanstack/start@latest my-flexkit-app -- --package-manager npm
cd my-flexkit-appyarn
yarn create @tanstack/start@latest my-flexkit-app --package-manager yarn
cd my-flexkit-appbun
bun create @tanstack/start@latest my-flexkit-app --package-manager bun
cd my-flexkit-appInstall Flexkit Studio packages
pnpm
pnpm add @flexkit/studio @flexkit/schemanpm
npm install @flexkit/studio @flexkit/schemayarn
yarn add @flexkit/studio @flexkit/schemabun
bun add @flexkit/studio @flexkit/schemaCreate the Flexkit configuration
Create flexkit.config.ts in the project root. This file is pure data with no React or JSX: the CLI deploys it, and the Studio renders the deployed result.
import { defineConfig, defineEntity } from '@flexkit/schema';
const categories = defineEntity({
name: 'category',
plural: 'categories',
display: 'name',
menu: { label: 'Categories', icon: 'Tag' },
attributes: [
{ name: 'name', label: 'Name', scope: 'local', dataType: 'string', inputType: 'text', searchable: true },
{ name: 'slug', label: 'Slug', scope: 'global', dataType: 'string', inputType: 'text', unique: true },
],
});
const products = defineEntity({
name: 'product',
plural: 'products',
display: 'name',
menu: { label: 'Products', icon: 'ShoppingCart' },
attributes: [
{ name: 'name', label: 'Name', scope: 'local', dataType: 'string', inputType: 'text', searchable: true },
{
name: 'sku',
label: 'SKU',
scope: 'global',
dataType: 'string',
inputType: 'text',
unique: true,
searchable: true,
},
{ name: 'price', label: 'Price', scope: 'local', dataType: 'float', inputType: 'number' },
{ name: 'image', label: 'Image', scope: 'global', dataType: 'asset', inputType: 'asset' },
{ name: 'status', label: 'Status', scope: 'global', dataType: 'string', inputType: 'text', defaultValue: 'draft' },
{
name: 'category',
label: 'Category',
scope: 'relationship',
dataType: 'string',
inputType: 'relationship',
relationship: { mode: 'single', field: 'name', entity: 'category' },
},
],
});
export default defineConfig({
title: 'Catalog Studio',
projectId: 'your-project-id',
scopes: [{ name: 'default', label: 'Default', isDefault: true }],
schema: [products, categories],
});'your-project-id' with your Flexkit project ID.local fields use the selected scope in Studio. If the active scope does not have a local value yet, the default scope value is shown instead.
Create TanStack Start routes for Studio and API
Create the following files:
- index.tsx
- studio.tsx
- $.tsx
- $.tsx
import { createFileRoute, redirect } from '@tanstack/react-router';
export const Route = createFileRoute('/')({
beforeLoad: () => {
throw redirect({ to: '/studio' });
},
});import { createFileRoute } from '@tanstack/react-router';
import '@flexkit/studio/styles.css';
import { FlexkitStudio } from '@flexkit/studio';
export const Route = createFileRoute('/studio')({
ssr: false,
component: StudioPage,
});
function StudioPage() {
return <FlexkitStudio projectIds={['your-project-id']} basename="/studio" />;
}import { createFileRoute } from '@tanstack/react-router';
import '@flexkit/studio/styles.css';
import { FlexkitStudio } from '@flexkit/studio';
export const Route = createFileRoute('/studio/$')({
ssr: false,
component: StudioPage,
});
function StudioPage() {
return <FlexkitStudio projectIds={['your-project-id']} basename="/studio" />;
}import { createFileRoute } from '@tanstack/react-router';
import { createFlexkitFetchHandler } from '@flexkit/studio/tanstack-start';
const flexkitHandler = createFlexkitFetchHandler();
export const Route = createFileRoute('/api/flexkit/$')({
component: () => null,
server: {
handlers: {
GET: async ({ request }) => {
return flexkitHandler(request);
},
POST: async ({ request }) => {
return flexkitHandler(request);
},
PUT: async ({ request }) => {
return flexkitHandler(request);
},
PATCH: async ({ request }) => {
return flexkitHandler(request);
},
DELETE: async ({ request }) => {
return flexkitHandler(request);
},
},
},
});Deploy your data schema
flexkit login
flexkit whoami
flexkit deployThe Studio renders the deployed schema, not the local file. Run flexkit deploy again after every change to
flexkit.config.ts; until the first deployment the project has no entities, so Desk is empty.
Run your project
pnpm
pnpm devnpm
npm run devyarn
yarn devbun
bun run devOpen your browser and navigate to http://localhost:3000/studio.
Confirm your first success
Sign into Studio with the account that has access to the configured project. Open Desk, create a Category and Product, save, and reopen the record. Open Explorer and run the tutorial read. The record ID and global SKU should agree with the saved record.
If the UI loads but data does not, check the project ID, completed schema deployment, role/spaces, and API handler route. An empty Desk means flexkit deploy has not run for that project ID yet; a Project not found screen means the project ID is wrong or the API handler route is not mounted; a No access screen means your account is not a member of the project. Continue with the sample store tour to see what the agent can do, then production deployment.