Integration
How to integrate FoundryKit components with primitives
Integration with Primitives
FoundryKit components are built on top of primitives and are designed to work seamlessly together. This guide shows you how to integrate components with primitives effectively.
Basic Integration
Using Components with Primitives
Components can be used alongside primitives in the same interface:
import { Button, Input, Card, CardContent, CardHeader, CardTitle } from '@foundrykit/primitives'
import { SearchBar, Calendar } from '@foundrykit/components'
import { useState } from 'react'
function IntegratedInterface() {
const [date, setDate] = useState<Date>()
const [searchQuery, setSearchQuery] = useState('')
return (
<Card className="w-full max-w-2xl">
<CardHeader>
<CardTitle>Search and Filter</CardTitle>
</CardHeader>
<CardContent className="space-y-4">
<div className="space-y-2">
<label className="text-sm font-medium">Search</label>
<SearchBar
placeholder="Search items..."
onSearch={setSearchQuery}
/>
</div>
<div className="space-y-2">
<label className="text-sm font-medium">Select Date</label>
<Calendar
mode="single"
selected={date}
onSelect={setDate}
className="rounded-md border"
/>
</div>
<div className="flex space-x-2">
<Button>Apply Filters</Button>
<Button variant="outline">Clear All</Button>
</div>
</CardContent>
</Card>
)
}
Form Integration
Create forms that combine primitives and components:
import { Button, Input, Label, Textarea, Card, CardContent, CardHeader, CardTitle } from '@foundrykit/primitives'
import { DatePicker } from '@foundrykit/components'
import { useState } from 'react'
function IntegratedForm() {
const [formData, setFormData] = useState({
title: '',
description: '',
dueDate: undefined as Date | undefined,
priority: 'medium'
})
const handleSubmit = (e: React.FormEvent) => {
e.preventDefault()
console.log('Form submitted:', formData)
}
return (
<Card className="w-full max-w-md">
<CardHeader>
<CardTitle>Create Task</CardTitle>
</CardHeader>
<CardContent>
<form onSubmit={handleSubmit} className="space-y-4">
<div className="space-y-2">
<Label htmlFor="title">Task Title</Label>
<Input
id="title"
value={formData.title}
onChange={(e) => setFormData(prev => ({ ...prev, title: e.target.value }))}
required
/>
</div>
<div className="space-y-2">
<Label htmlFor="description">Description</Label>
<Textarea
id="description"
value={formData.description}
onChange={(e) => setFormData(prev => ({ ...prev, description: e.target.value }))}
rows={3}
/>
</div>
<div className="space-y-2">
<Label>Due Date</Label>
<DatePicker
selected={formData.dueDate}
onSelect={(date) => setFormData(prev => ({ ...prev, dueDate: date }))}
placeholder="Select due date"
/>
</div>
<div className="flex space-x-2">
<Button type="submit" className="flex-1">Create Task</Button>
<Button type="button" variant="outline">Cancel</Button>
</div>
</form>
</CardContent>
</Card>
)
}
Advanced Integration Patterns
Data Table with Search and Pagination
Create a complete data table interface:
import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow, Card, CardContent, CardHeader, CardTitle } from '@foundrykit/primitives'
import { SearchBar, Pagination } from '@foundrykit/components'
import { useState, useMemo } from 'react'
interface User {
id: string
name: string
email: string
role: string
}
function DataTable({ users }: { users: User[] }) {
const [searchQuery, setSearchQuery] = useState('')
const [currentPage, setCurrentPage] = useState(1)
const itemsPerPage = 10
const filteredUsers = useMemo(() => {
return users.filter(user =>
user.name.toLowerCase().includes(searchQuery.toLowerCase()) ||
user.email.toLowerCase().includes(searchQuery.toLowerCase())
)
}, [users, searchQuery])
const totalPages = Math.ceil(filteredUsers.length / itemsPerPage)
const paginatedUsers = filteredUsers.slice(
(currentPage - 1) * itemsPerPage,
currentPage * itemsPerPage
)
return (
<Card>
<CardHeader>
<CardTitle>Users</CardTitle>
<SearchBar
placeholder="Search users..."
onSearch={setSearchQuery}
/>
</CardHeader>
<CardContent>
<Table>
<TableHeader>
<TableRow>
<TableHead>Name</TableHead>
<TableHead>Email</TableHead>
<TableHead>Role</TableHead>
</TableRow>
</TableHeader>
<TableBody>
{paginatedUsers.map(user => (
<TableRow key={user.id}>
<TableCell>{user.name}</TableCell>
<TableCell>{user.email}</TableCell>
<TableCell>{user.role}</TableCell>
</TableRow>
))}
</TableBody>
</Table>
<div className="mt-4">
<Pagination
currentPage={currentPage}
totalPages={totalPages}
onPageChange={setCurrentPage}
/>
</div>
</CardContent>
</Card>
)
}
Dashboard Layout
Create a dashboard that combines multiple components and primitives:
import { Card, CardContent, CardHeader, CardTitle, Button, Badge, Separator } from '@foundrykit/primitives'
import { Calendar, SearchBar, Command, CommandInput, CommandList, CommandItem } from '@foundrykit/components'
import { useState } from 'react'
function Dashboard() {
const [selectedDate, setSelectedDate] = useState<Date>()
const [searchQuery, setSearchQuery] = useState('')
return (
<div className="grid grid-cols-1 lg:grid-cols-3 gap-6 p-6">
{/* Main Content */}
<div className="lg:col-span-2 space-y-6">
<Card>
<CardHeader>
<CardTitle>Quick Actions</CardTitle>
</CardHeader>
<CardContent>
<SearchBar
placeholder="Search or type commands..."
onSearch={setSearchQuery}
/>
<div className="mt-4 flex space-x-2">
<Button>New Project</Button>
<Button variant="outline">Import Data</Button>
<Button variant="outline">Export Report</Button>
</div>
</CardContent>
</Card>
<Card>
<CardHeader>
<CardTitle>Recent Activity</CardTitle>
</CardHeader>
<CardContent>
<div className="space-y-4">
{[1, 2, 3].map(i => (
<div key={i} className="flex items-center space-x-4">
<Badge variant="secondary">New</Badge>
<div className="flex-1">
<p className="text-sm font-medium">Project {i} updated</p>
<p className="text-sm text-muted-foreground">2 hours ago</p>
</div>
<Button variant="ghost" size="sm">View</Button>
</div>
))}
</div>
</CardContent>
</Card>
</div>
{/* Sidebar */}
<div className="space-y-6">
<Card>
<CardHeader>
<CardTitle>Calendar</CardTitle>
</CardHeader>
<CardContent>
<Calendar
mode="single"
selected={selectedDate}
onSelect={setSelectedDate}
className="rounded-md border"
/>
</CardContent>
</Card>
<Card>
<CardHeader>
<CardTitle>Quick Commands</CardTitle>
</CardHeader>
<CardContent>
<Command>
<CommandInput placeholder="Type a command..." />
<CommandList>
<CommandItem>Open Settings</CommandItem>
<CommandItem>New Task</CommandItem>
<CommandItem>View Analytics</CommandItem>
<CommandItem>Export Data</CommandItem>
</CommandList>
</Command>
</CardContent>
</Card>
</div>
</div>
)
}
Styling Integration
Consistent Theming
Ensure components and primitives use consistent theming:
import { Button, Card, CardContent, CardHeader, CardTitle } from '@foundrykit/primitives'
import { Calendar, SearchBar } from '@foundrykit/components'
function ThemedInterface() {
return (
<div className="space-y-6">
{/* Use consistent border radius and spacing */}
<Card className="rounded-lg border shadow-sm">
<CardHeader className="pb-4">
<CardTitle className="text-lg font-semibold">Search Interface</CardTitle>
</CardHeader>
<CardContent className="space-y-4">
<SearchBar
placeholder="Search..."
className="rounded-md"
/>
<Calendar
mode="single"
className="rounded-md border"
/>
<div className="flex space-x-2">
<Button className="rounded-md">Search</Button>
<Button variant="outline" className="rounded-md">Clear</Button>
</div>
</CardContent>
</Card>
</div>
)
}
Custom CSS Variables
Use CSS variables for consistent theming across components and primitives:
:root {
/* Shared color palette */
--color-primary: oklch(0.55 0.15 250);
--color-primary-foreground: oklch(0.98 0.005 250);
--color-secondary: oklch(0.95 0.01 250);
--color-secondary-foreground: oklch(0.15 0.08 250);
/* Shared spacing */
--spacing-1: 0.25rem;
--spacing-2: 0.5rem;
--spacing-3: 0.75rem;
--spacing-4: 1rem;
/* Shared border radius */
--radius: 0.5rem;
--radius-lg: 0.75rem;
/* Shared shadows */
--shadow-sm: 0 1px 2px 0 rgb(0 0 0 / 0.05);
--shadow-md: 0 4px 6px -1px rgb(0 0 0 / 0.1);
}
Performance Integration
Lazy Loading Components
Lazy load components that aren't immediately needed:
import { lazy, Suspense } from 'react'
import { Card, CardContent, Skeleton } from '@foundrykit/primitives'
const Calendar = lazy(() => import('@foundrykit/components').then(m => ({ default: m.Calendar })))
const Command = lazy(() => import('@foundrykit/components').then(m => ({ default: m.Command })))
function LazyLoadedInterface() {
return (
<div className="space-y-6">
<Card>
<CardContent>
<Suspense fallback={<Skeleton className="h-32 w-full" />}>
<Calendar mode="single" />
</Suspense>
</CardContent>
</Card>
<Card>
<CardContent>
<Suspense fallback={<Skeleton className="h-20 w-full" />}>
<Command>
<CommandInput placeholder="Loading commands..." />
</Command>
</Suspense>
</CardContent>
</Card>
</div>
)
}
Memoized Integration
Optimize performance with memoization:
import { memo, useMemo } from 'react'
import { Card, CardContent, CardHeader, CardTitle } from '@foundrykit/primitives'
import { SearchBar, Pagination } from '@foundrykit/components'
const MemoizedSearchBar = memo(SearchBar)
const MemoizedPagination = memo(Pagination)
function OptimizedInterface({ data, onSearch, onPageChange }) {
const searchConfig = useMemo(() => ({
placeholder: "Search data...",
debounceMs: 300
}), [])
const paginationConfig = useMemo(() => ({
showFirstLast: true,
showPrevNext: true,
maxVisible: 5
}), [])
return (
<Card>
<CardHeader>
<CardTitle>Data Interface</CardTitle>
</CardHeader>
<CardContent className="space-y-4">
<MemoizedSearchBar
{...searchConfig}
onSearch={onSearch}
/>
{/* Data content */}
<div className="min-h-[200px]">
{data.map(item => (
<div key={item.id} className="p-2 border-b">
{item.name}
</div>
))}
</div>
<MemoizedPagination
{...paginationConfig}
currentPage={currentPage}
totalPages={totalPages}
onPageChange={onPageChange}
/>
</CardContent>
</Card>
)
}
Accessibility Integration
Keyboard Navigation
Ensure proper keyboard navigation between components and primitives:
import { Button, Card, CardContent, CardHeader, CardTitle } from '@foundrykit/primitives'
import { SearchBar, Calendar } from '@foundrykit/components'
function AccessibleInterface() {
return (
<Card>
<CardHeader>
<CardTitle>Accessible Interface</CardTitle>
</CardHeader>
<CardContent className="space-y-4">
<div>
<label htmlFor="search" className="sr-only">Search</label>
<SearchBar
id="search"
placeholder="Search..."
onSearch={handleSearch}
/>
</div>
<div>
<label className="sr-only">Select date</label>
<Calendar
mode="single"
selected={date}
onSelect={setDate}
className="rounded-md border"
/>
</div>
<div className="flex space-x-2">
<Button>Apply</Button>
<Button variant="outline">Reset</Button>
</div>
</CardContent>
</Card>
)
}
Next Steps
- Explore advanced patterns for complex integration scenarios
- Review best practices for optimal integration
- Learn about component composition for building complex interfaces