| name | frontend-developer |
| description | Build user interfaces using Redpanda UI Registry components with React, TypeScript, and Vitest testing. Use when user requests UI components, pages, forms, or mentions 'build UI', 'create component', 'design system', 'frontend', or 'registry'. |
Frontend UI Development
Build UIs using the Redpanda UI Registry design system following this repo's patterns.
Critical Rules
ALWAYS:
- Fetch registry docs FIRST: Invoke
mcp__context7__get-library-docs with library /websites/redpanda-ui-registry_netlify_app
- Use registry components from
src/components/redpanda-ui/
- Install components via CLI:
yes | bunx @fumadocs/cli add --dir https://redpanda-ui-registry.netlify.app/r <component>
- Use functional React components with TypeScript
- Run
bun i --yarn after installing packages
NEVER:
- Use deprecated
@redpanda-data/ui or Chakra UI
- Modify files in registry base directory (check
cli.json)
- Copy/paste registry source code
- Add margin
className directly to registry components
- Use inline
style prop on registry components
- Leave
console.log or comments in code
Workflow
1. Fetch Registry Documentation
Invoke: mcp__context7__get-library-docs
Library: /websites/redpanda-ui-registry_netlify_app
2. Install Components
yes | bunx @fumadocs/cli add --dir https://redpanda-ui-registry.netlify.app/r button
yes | bunx @fumadocs/cli add --dir https://redpanda-ui-registry.netlify.app/r card dialog form
bun i && bun i --yarn
3. Write Component
Structure:
interface Props {
title: string
onSubmit: (data: FormData) => void
}
export function MyComponent({ title, onSubmit }: Props) {
}
Styling:
<Button variant="primary" size="lg">Click</Button>
<div className="mt-4">
<Button variant="primary">Click</Button>
</div>
<Button className="mt-4" variant="primary">Click</Button>
TypeScript:
- Define prop interfaces
- Never use
any - infer correct types
- Use generics for collections
Performance:
- Hoist static content outside component
- Use
useMemo for expensive computations - but only when there's a noticeable performance impact
- Use
memo for components receiving props
4. Write Tests
Test types:
- Unit tests (
.test.ts): Pure logic, utilities, helpers - Node environment
- Integration tests (
.test.tsx): React components, UI interactions - JSDOM environment
Unit test example:
import { formatNumber } from './utils'
describe('formatNumber', () => {
test('should format numbers with commas', () => {
expect(formatNumber(1000)).toBe('1,000')
})
})
Integration test example:
import { render, screen, fireEvent, waitFor } from 'test-utils';
import { createRouterTransport } from '@connectrpc/connect';
import { createPipeline } from 'protogen/redpanda/api/console/v1alpha1/pipeline-PipelineService_connectquery';
import { MyComponent } from './component';
describe('MyComponent', () => {
test('should trigger gRPC mutation when form is submitted', async () => {
const mockCreatePipeline = vi.fn(() =>
Promise.resolve({ id: '123', name: 'test-pipeline' })
);
const transport = createRouterTransport(({ rpc }) => {
rpc(createPipeline, mockCreatePipeline);
});
render(<MyComponent />, { transport });
fireEvent.change(screen.getByLabelText('Pipeline Name'), {
target: { value: 'test-pipeline' }
});
fireEvent.click(screen.getByRole('button', { name: 'Create' }));
await waitFor(() => {
expect(mockCreatePipeline).toHaveBeenCalledWith({
name: 'test-pipeline'
});
});
});
});
Mocking:
vi.mock('module-name', async (importOriginal) => {
const actual = await importOriginal<typeof import('module-name')>()
return {
...actual,
functionToMock: vi.fn(),
}
})
const mockFunction = vi.mocked(functionToMock)
5. Validation
bun run type:check
bun run test
bun run lint
bun run build
bun run start2 --port=3004
Success criteria:
- โ No TypeScript errors
- โ All tests passing
- โ No lint errors
- โ Build succeeds
- โ No runtime errors in browser
Testing Commands
bun run test
bun run test:unit
bun run test:integration
bun run test:watch
bun run test:coverage
Common Patterns
Registry Component Usage
Check src/components/redpanda-ui/ for installed components before installing new ones.
Use component variants instead of custom styling:
<Button variant="primary" size="lg" />
<Card variant="outline" />
<Dialog size="md" />
Form Patterns
For complex forms, use React Hook Form + Zod:
import { useForm } from 'react-hook-form'
import { zodResolver } from '@hookform/resolvers/zod'
import { z } from 'zod'
const schema = z.object({
name: z.string().min(1),
email: z.string().email(),
})
const form = useForm({
resolver: zodResolver(schema),
defaultValues: { name: '', email: '' },
})
For simple forms, use AutoForm:
import { AutoForm } from '@autoform/react';
import { ZodProvider } from '@autoform/zod';
<AutoForm
schema={schema}
onSubmit={handleSubmit}
provider={ZodProvider}
/>
Reusable Sub-Components
Create sub-components in the same file when logic repeats:
export function FeatureComponent() {
return (
<div>
<FeatureHeader />
<FeatureContent />
</div>
);
}
function FeatureHeader() { }
function FeatureContent() { }
Testing Best Practices
File naming:
.test.ts for utilities and pure functions (unit tests)
.test.tsx for React components (integration tests)
Mock location:
- Use pre-configured mocks from
src/test-utils/
- Mock external dependencies, not internal code
Render utility:
- Import from
test-utils/test-utils.tsx for component tests
- Includes all providers (React Query, Router, Auth0)
Async testing:
- Use
waitFor for async operations
- Test loading and error states
- Verify state updates
Quick Reference
Check before installing:
ls src/components/redpanda-ui/components/
Multi-component install:
yes | bunx @fumadocs/cli add --dir https://redpanda-ui-registry.netlify.app/r \
button card dialog form input label select
After package install:
bun i && bun i --yarn
Testing shortcuts:
- Unit test: Pure function? Use
.test.ts
- Integration test: React component? Use
.test.tsx
- Mock external: Add to
src/test-utils/
- Custom render: Use
test-utils/test-utils.tsx
Output
After completing work:
- Confirm all validation steps passed
- Summarize what was built
- Note any runtime checks needed in browser
- Mention test coverage if relevant