Implement frontend designs from figma using Chakra UI v3 and Storybook
92
92%
Does it follow best practices?
Impact
92%
1.64xAverage score across 6 eval scenarios
Advisory
Suggest reviewing before use
Page components are the top-level assembly layer. They compose composite and low-level components, own data loading and mutations, and define layout — but contain no UI of their own. Any visual element (buttons, cards, forms, tables) should be a composite or low-level component, not inline JSX in the page.
src/routes/ for existing pages with similar patterns (data loading, layout, mutations).src/components/ — the page should assemble these, not rebuild them.src/components/layout/PageLayout/ — all pages wrap content in <PageLayout>.Before writing code, identify:
| Belongs here | Does NOT belong here |
|---|---|
| Route definition and loader | Styled UI elements (buttons, cards, badges) |
| Data fetching via query options | Business logic for rendering states |
| Mutations and cache invalidation | Reusable form fields |
Layout with VStack/HStack/SimpleGrid | Complex conditional rendering trees |
Feature flag checks in beforeLoad | Hardcoded visual styles |
| Breadcrumb configuration | Component-level state management |
Pages live in src/routes/ following TanStack Router's file-based routing:
src/routes/_app/_authed/workspaces/$workspace/my-page/
├── index.tsx # The page component
└── route.lazy.tsx # Lazy-loaded route (if needed)Use TanStack Router's loader with queryClient.ensureQueryData() to pre-fetch data before navigation. This prevents loading spinners on page transitions.
import { createFileRoute } from '@tanstack/react-router';
export const Route = createFileRoute('/_app/_authed/workspaces/$workspace/my-page/')({
loader: async ({ context: { queryClient }, params: { workspace } }) => {
const { data } = await queryClient.ensureQueryData(
getMyDataQueryOptions(workspace)
);
return { items: data };
},
component: MyPage,
});Access loaded data in the component:
function MyPage() {
const { items } = Route.useLoaderData();
// ...
}Use useMutation() with $api.mutationOptions(). Always invalidate related queries after a successful mutation.
import { useMutation, useQueryClient } from '@tanstack/react-query';
import { $api } from '~/lib/api/client';
function MyPage() {
const queryClient = useQueryClient();
const { mutateAsync } = useMutation(
$api.mutationOptions('post', '/api/path/{id}')
);
async function handleAction(id: string) {
await mutateAsync({ params: { path: { id } }, body: { /* ... */ } });
await queryClient.invalidateQueries({ queryKey: ['relevant-key'] });
}
}For form submissions, use useTesslForm which wraps mutations with automatic error handling:
import { useTesslForm } from '~/lib/form/use-tessl-form';
import { Form } from '~/lib/form/components/form';
const form = useTesslForm({
defaultValues: { name: '' },
onSubmit: async ({ value }) => {
await mutateAsync({ body: value });
await queryClient.invalidateQueries(/* ... */);
},
});Pages wrap all content in <PageLayout> and use Chakra layout primitives to arrange child components. The page itself should read like a table of contents — a list of composed components with layout between them.
import { VStack, HStack, SimpleGrid } from '@chakra-ui/react';
import { PageLayout } from '~/components/layout/PageLayout/PageLayout';
function MyPage() {
const { items } = Route.useLoaderData();
return (
<PageLayout>
<VStack gap="6" align="stretch">
<MyPageHeader />
<SimpleGrid columns={{ base: 1, md: 2, lg: 3 }} gap="4">
{items.map((item) => (
<MyItemCard key={item.id} item={item} />
))}
</SimpleGrid>
</VStack>
</PageLayout>
);
}Gate pages behind feature flags in beforeLoad:
beforeLoad: ({ context }) => {
assertFeatureFlagEnabled(context, 'my-feature');
},Configure breadcrumbs in the loader return value — <PageLayout> renders them automatically from the route hierarchy.
When building from a Figma design, follow this comparison process:
get_screenshot(fileKey, nodeId) from the Figma MCP server. This is the visual source of truth.cd apps/frontend && bun run dev:server --port XXXX (random port 3100–3999).playwright-cli screenshot <url> — and compare side-by-side with the Figma screenshot.Page-level Figma comparison focuses on layout and composition, not individual component styling (that's verified at the composite/low-level tier):
Test data loading and navigation behaviour:
// my-page.test.tsx
import { render, screen } from '~/test/utils';
test('loads and displays items', async () => {
// Mock the API response
// Render the route
// Assert that composite components receive the correct data
});useTesslForm error handling or error boundaries)Composition:
Data:
ensureQueryData for pre-fetchinguseTesslForm for automatic error handling<form.SubmissionError /> included for form pagesLayout:
<PageLayout>VStack/HStack/SimpleGrid — no custom CSSFigma Comparison (if applicable):