.cursor/skills/component-reuse-workflow/SKILL.md
Workflow that enforces component reuse by systematically searching for existing components before creating new ones. Always use existing UI components, patterns, and utilities. Only create new components when no suitable existing piece exists, and even then build on established patterns (Dialog, Sheet, etc.) instead of inventing new ones.
npx skillsauth add ripgraphics/authorsinfo component-reuse-workflowInstall this skill globally with one command. Works with Claude Code, Cursor, and Windsurf.
3 of 9 scanners reported clean
Some scanners were skipped, did not run, or reported a non-clean status. Review each row below.
This workflow enforces a component-first approach to development. Before writing any new component or UI code, you must systematically search for and evaluate existing components. Only create new components when absolutely necessary, and always build on established patterns.
components/ui/ components before building custom solutionsBEFORE writing any component code, you MUST:
# List all available UI components
ls components/ui/
# Search for component names matching your need
grep -r "modal\|dialog\|sheet\|popover\|drawer" components/ui/ -i
Key UI Components to Check:
dialog.tsx, alert-dialog.tsx, sheet.tsxform.tsx, input.tsx, textarea.tsx, select.tsx, checkbox.tsx, radio-group.tsxcard.tsx, tabs.tsx, accordion.tsx, collapsible.tsx, separator.tsxdropdown-menu.tsx, context-menu.tsx, navigation-menu.tsx, breadcrumb.tsxtoast.tsx, alert.tsx, progress.tsx, skeleton.tsxtable.tsx, badge.tsx, avatar.tsx, chart.tsxpopover.tsx, hover-card.tsx, tooltip.tsx# Check for domain-specific components
ls components/enterprise/
ls components/admin/
ls components/group/
ls components/entity/
ls components/feed/
Use codebase search to find similar features:
Look for:
For each potential existing component, evaluate:
┌─────────────────────────────────────────────────────────────┐
│ Component Decision Flow │
├─────────────────────────────────────────────────────────────┤
│ │
│ 1. Found exact match? → USE IT │
│ │
│ 2. Found near match? → EXTEND IT (add props, customize) │
│ │
│ 3. Found similar pattern? → BUILD ON PATTERN │
│ (e.g., need custom modal → extend Dialog) │
│ │
│ 4. Nothing found? → CREATE NEW (but build on patterns) │
│ │
└─────────────────────────────────────────────────────────────┘
Example: Need a modal
// ✅ CORRECT: Use existing Dialog
import { Dialog, DialogContent, DialogHeader, DialogTitle } from '@/components/ui/dialog'
// ❌ WRONG: Create new Modal component
// Don't create components/ui/custom-modal.tsx
Example: Need a form
// ✅ CORRECT: Use existing Form components
import { Form, FormField, FormItem, FormLabel, FormControl } from '@/components/ui/form'
import { Input } from '@/components/ui/input'
import { Button } from '@/components/ui/button'
// ❌ WRONG: Create custom form components
When existing component is close but needs customization:
// ✅ CORRECT: Extend Dialog with custom props/styling
import { Dialog, DialogContent } from '@/components/ui/dialog'
<Dialog open={open} onOpenChange={setOpen}>
<DialogContent className="max-w-4xl h-[90vh] flex flex-col">
{/* Custom content using existing Dialog pattern */}
</DialogContent>
</Dialog>
If you MUST create a new component, build on existing patterns:
Example: Creating a custom drawer component
// ✅ CORRECT: Build on Sheet pattern (which is built on Dialog)
'use client'
import * as React from 'react'
import * as SheetPrimitive from '@radix-ui/react-dialog'
import { cn } from '@/lib/utils'
import { CloseButton } from '@/components/ui/close-button'
// Follow the same pattern as sheet.tsx
const Drawer = SheetPrimitive.Root
const DrawerTrigger = SheetPrimitive.Trigger
// ... follow existing patterns
Example: Creating a specialized dialog variant
// ✅ CORRECT: Compose existing Dialog with custom wrapper
import { Dialog, DialogContent, DialogHeader, DialogTitle } from '@/components/ui/dialog'
import { Button } from '@/components/ui/button'
export function ConfirmationDialog({ ... }) {
return (
<Dialog>
<DialogContent>
<DialogHeader>
<DialogTitle>{title}</DialogTitle>
</DialogHeader>
{/* Custom content */}
</DialogContent>
</Dialog>
)
}
❌ DON'T:
✅ DO:
This project uses Dialog from components/ui/dialog.tsx as the primary modal pattern:
@radix-ui/react-dialogUse Dialog for:
Don't use:
Use Sheet from components/ui/sheet.tsx for:
Use AlertDialog from components/ui/alert-dialog.tsx for:
This project uses Form from components/ui/form.tsx with:
# Find all dialog/modal usage
grep -r "from.*dialog" components/ app/ --include="*.tsx" --include="*.ts"
# Find all form implementations
grep -r "FormField\|FormItem" components/ app/ --include="*.tsx"
# Find all button usage patterns
grep -r "Button\|button" components/ui/ --include="*.tsx" | head -20
# List all UI components
find components/ui -name "*.tsx" -type f | sort
Use codebase search for:
Before creating any new component, verify:
components/ui/ for existing componentcomponents/enterprise/, components/admin/, etc.)A new component is only justified when:
// components/enterprise/create-post-modal.tsx
import { Dialog, DialogContent, DialogHeader, DialogTitle } from '@/components/ui/dialog'
import { Form, FormField } from '@/components/ui/form'
import { Button } from '@/components/ui/button'
export function CreatePostModal({ open, onOpenChange }) {
return (
<Dialog open={open} onOpenChange={onOpenChange}>
<DialogContent className="max-w-2xl">
<DialogHeader>
<DialogTitle>Create Post</DialogTitle>
</DialogHeader>
<Form>
{/* Form fields using existing Input, Textarea, etc. */}
</Form>
</DialogContent>
</Dialog>
)
}
// Custom variant of Dialog for full-screen modals
import { Dialog, DialogContent } from '@/components/ui/dialog'
export function FullScreenDialog({ children, ...props }) {
return (
<Dialog {...props}>
<DialogContent className="max-w-full h-full max-h-screen flex flex-col">
{children}
</DialogContent>
</Dialog>
)
}
// ❌ DON'T: Create new modal when Dialog exists
export function CustomModal({ open, onClose, children }) {
return (
<div className="fixed inset-0 z-50">
<div className="modal-content">
{children}
</div>
</div>
)
}
// ❌ DON'T: Create modal without using Dialog pattern
export function MyModal() {
// Custom implementation that doesn't use Dialog
}
enterprise/, admin/, etc.Before writing component code:
ls components/ui/ - List available UI componentsgrep -r "component-name" components/ - Search for usageRemember: Search first, extend second, create last.
CRITICAL: All components must be fully reusable and work anywhere in the application without modification. This means components should be presentation-focused and data-agnostic.
Components that only render UI and accept all data via props:
// ✅ CORRECT: Fully reusable presentational component
interface FeedCardProps {
post: {
id: string
text: string
author: { id: string; name: string; avatar: string }
engagement: { likes: number; comments: number; shares: number }
createdAt: string
}
currentUserId?: string
onLike?: (postId: string) => void
onComment?: (postId: string) => void
onShare?: (postId: string) => void
className?: string
}
export function FeedCard({ post, currentUserId, onLike, onComment, onShare, className }: FeedCardProps) {
// ✅ Pure presentation - no data fetching, no hooks, no API calls
return (
<Card className={className}>
<CardHeader>
<Avatar src={post.author.avatar} />
<CardTitle>{post.author.name}</CardTitle>
</CardHeader>
<CardContent>
<p>{post.text}</p>
<EngagementBar
likes={post.engagement.likes}
comments={post.engagement.comments}
shares={post.engagement.shares}
onLike={() => onLike?.(post.id)}
onComment={() => onComment?.(post.id)}
onShare={() => onShare?.(post.id)}
/>
</CardContent>
</Card>
)
}
Usage anywhere:
// Can be used in any page, any context
<FeedCard
post={postData}
currentUserId={userId}
onLike={handleLike}
onComment={handleComment}
onShare={handleShare}
/>
Separate container components handle data fetching and pass to presentational components:
// ✅ CORRECT: Container component handles data fetching
'use client'
import { useEffect, useState } from 'react'
import { FeedCard } from './feed-card' // Presentational component
import { useAuth } from '@/hooks/useAuth'
export function FeedCardContainer({ postId }: { postId: string }) {
const { user } = useAuth()
const [post, setPost] = useState(null)
const [loading, setLoading] = useState(true)
useEffect(() => {
fetch(`/api/posts/${postId}`)
.then(r => r.json())
.then(data => {
setPost(data)
setLoading(false)
})
}, [postId])
const handleLike = async (id: string) => {
await fetch(`/api/posts/${id}/like`, { method: 'POST' })
// Refresh data
}
if (loading) return <Skeleton />
if (!post) return null
// ✅ Presentational component receives all data via props
return (
<FeedCard
post={post}
currentUserId={user?.id}
onLike={handleLike}
onComment={handleComment}
onShare={handleShare}
/>
)
}
Components that mix data fetching with presentation are NOT reusable:
// ❌ WRONG: Component has hardcoded API calls
export function FeedCard({ postId }: { postId: string }) {
const [post, setPost] = useState(null)
useEffect(() => {
// ❌ Hardcoded API endpoint - not reusable
fetch(`/api/posts/${postId}`)
.then(r => r.json())
.then(setPost)
}, [postId])
// ❌ Hardcoded engagement API - not reusable
const handleLike = async () => {
await fetch(`/api/posts/${postId}/like`, { method: 'POST' })
}
return <Card>...</Card>
}
Problems:
Red Flags:
fetch() calls inside componentuseAuth() or other hooks that fetch data/api/...)Before (Non-Reusable):
// ❌ Component fetches its own data
export function UserProfile({ userId }: { userId: string }) {
const [user, setUser] = useState(null)
useEffect(() => {
fetch(`/api/users/${userId}`)
.then(r => r.json())
.then(setUser)
}, [userId])
return <div>{user?.name}</div>
}
After (Reusable):
// ✅ Presentational component accepts data via props
interface UserProfileProps {
user: {
id: string
name: string
avatar?: string
bio?: string
}
className?: string
}
export function UserProfile({ user, className }: UserProfileProps) {
return (
<div className={className}>
<Avatar src={user.avatar} />
<h2>{user.name}</h2>
{user.bio && <p>{user.bio}</p>}
</div>
)
}
// ✅ Container component handles data fetching
export function UserProfileContainer({ userId }: { userId: string }) {
const [user, setUser] = useState(null)
useEffect(() => {
fetch(`/api/users/${userId}`)
.then(r => r.json())
.then(setUser)
}, [userId])
if (!user) return <Skeleton />
return <UserProfile user={user} />
}
Before (Non-Reusable):
// ❌ Component handles actions internally
export function LikeButton({ postId }: { postId: string }) {
const handleClick = async () => {
// ❌ Hardcoded API call
await fetch(`/api/posts/${postId}/like`, { method: 'POST' })
}
return <Button onClick={handleClick}>Like</Button>
}
After (Reusable):
// ✅ Component accepts callback via props
interface LikeButtonProps {
liked: boolean
count: number
onLike: () => void | Promise<void>
className?: string
}
export function LikeButton({ liked, count, onLike, className }: LikeButtonProps) {
return (
<Button
variant={liked ? 'default' : 'outline'}
onClick={onLike}
className={className}
>
<Heart className={liked ? 'fill-red-500' : ''} />
{count}
</Button>
)
}
// ✅ Container provides the callback
export function LikeButtonContainer({ postId }: { postId: string }) {
const [liked, setLiked] = useState(false)
const [count, setCount] = useState(0)
const handleLike = async () => {
await fetch(`/api/posts/${postId}/like`, { method: 'POST' })
setLiked(!liked)
setCount(prev => liked ? prev - 1 : prev + 1)
}
return <LikeButton liked={liked} count={count} onLike={handleLike} />
}
Before (Non-Reusable):
// ❌ Component directly uses hook
export function UserMenu() {
const { user } = useAuth() // ❌ Assumes UserContext exists
return <DropdownMenu>
<DropdownMenuItem>{user?.name}</DropdownMenuItem>
</DropdownMenu>
}
After (Reusable):
// ✅ Component accepts user data via props
interface UserMenuProps {
user: {
id: string
name: string
avatar?: string
}
onProfileClick?: () => void
onSettingsClick?: () => void
onLogout?: () => void
}
export function UserMenu({ user, onProfileClick, onSettingsClick, onLogout }: UserMenuProps) {
return (
<DropdownMenu>
<DropdownMenuTrigger>
<Avatar src={user.avatar} />
{user.name}
</DropdownMenuTrigger>
<DropdownMenuContent>
<DropdownMenuItem onClick={onProfileClick}>Profile</DropdownMenuItem>
<DropdownMenuItem onClick={onSettingsClick}>Settings</DropdownMenuItem>
<DropdownMenuItem onClick={onLogout}>Logout</DropdownMenuItem>
</DropdownMenuContent>
</DropdownMenu>
)
}
// ✅ Container provides user data
export function UserMenuContainer() {
const { user } = useAuth()
if (!user) return null
return (
<UserMenu
user={user}
onProfileClick={() => router.push(`/profile/${user.id}`)}
onSettingsClick={() => router.push('/settings')}
onLogout={handleLogout}
/>
)
}
Before creating or modifying a component, ensure:
Current (Non-Reusable):
// ❌ components/entity-feed-card.tsx - Has hardcoded API calls
export default function EntityFeedCard({ post }: { post: Post }) {
const fetchComments = useCallback(async () => {
// ❌ Hardcoded API endpoint
const response = await fetch(`/api/engagement?entity_id=${post.id}&entity_type=post`)
const data = await response.json()
setComments(data.recent_comments)
}, [post.id])
// ❌ Direct useAuth hook
const { user } = useAuth()
return <Card>...</Card>
}
Refactored (Reusable):
// ✅ Presentational component
interface FeedCardProps {
post: Post
comments?: Comment[]
currentUser?: User
onLoadComments?: (postId: string) => Promise<Comment[]>
onLike?: (postId: string) => Promise<void>
onComment?: (postId: string, text: string) => Promise<void>
className?: string
}
export function FeedCard({
post,
comments = [],
currentUser,
onLoadComments,
onLike,
onComment,
className
}: FeedCardProps) {
// ✅ All data and actions come from props
return <Card className={className}>...</Card>
}
// ✅ Container component handles data fetching
export function FeedCardContainer({ postId }: { postId: string }) {
const { user } = useAuth()
const [post, setPost] = useState<Post | null>(null)
const [comments, setComments] = useState<Comment[]>([])
useEffect(() => {
fetch(`/api/posts/${postId}`).then(r => r.json()).then(setPost)
}, [postId])
const handleLoadComments = async (id: string) => {
const response = await fetch(`/api/engagement?entity_id=${id}&entity_type=post`)
const data = await response.json()
setComments(data.recent_comments)
return data.recent_comments
}
if (!post) return <Skeleton />
return (
<FeedCard
post={post}
comments={comments}
currentUser={user}
onLoadComments={handleLoadComments}
onLike={handleLike}
onComment={handleComment}
/>
)
}
Current (Non-Reusable):
// ❌ Has hardcoded API calls
export function EnterpriseLinkPreviewCard({ url }: { url: string }) {
useEffect(() => {
// ❌ Hardcoded API endpoint
fetch('/api/link-preview', {
method: 'POST',
body: JSON.stringify({ url })
}).then(r => r.json()).then(setMetadata)
}, [url])
return <Card>...</Card>
}
Refactored (Reusable):
// ✅ Presentational component
interface LinkPreviewCardProps {
metadata: LinkPreviewMetadata
onRefresh?: () => Promise<LinkPreviewMetadata>
className?: string
}
export function LinkPreviewCard({ metadata, onRefresh, className }: LinkPreviewCardProps) {
return <Card className={className}>...</Card>
}
// ✅ Container component
export function LinkPreviewCardContainer({ url }: { url: string }) {
const [metadata, setMetadata] = useState<LinkPreviewMetadata | null>(null)
useEffect(() => {
fetch('/api/link-preview', {
method: 'POST',
body: JSON.stringify({ url })
}).then(r => r.json()).then(setMetadata)
}, [url])
const handleRefresh = async () => {
const response = await fetch('/api/link-preview', {
method: 'POST',
body: JSON.stringify({ url, refresh: true })
})
const data = await response.json()
setMetadata(data.data)
return data.data
}
if (!metadata) return <Skeleton />
return <LinkPreviewCard metadata={metadata} onRefresh={handleRefresh} />
}
Recommended structure for reusable components:
components/
ui/ # Pure UI primitives (Button, Dialog, etc.)
button.tsx # ✅ Fully reusable, no dependencies
dialog.tsx # ✅ Fully reusable, no dependencies
feed/ # Domain-specific presentational components
feed-card.tsx # ✅ Presentational - accepts data via props
feed-card-container.tsx # ✅ Container - handles data fetching
feed-list.tsx # ✅ Presentational - accepts items array
enterprise/ # Enterprise features
engagement-actions.tsx # ✅ Presentational - accepts callbacks
engagement-actions-container.tsx # ✅ Container - handles API calls
When refactoring existing components to be fully reusable:
✅ Works anywhere - Use in any page, any context, any app ✅ Easy to test - Test with mock data, no API mocks needed ✅ Flexible - Can be used with different data sources ✅ Maintainable - Clear separation of concerns ✅ Composable - Easy to combine with other components ✅ Type-safe - Props are fully typed ✅ Reusable - Write once, use everywhere
❌ Don't mix data fetching with presentation
// ❌ WRONG
export function Component() {
const [data, setData] = useState(null)
useEffect(() => {
fetch('/api/data').then(r => r.json()).then(setData)
}, [])
return <div>{data?.name}</div>
}
❌ Don't use hooks directly in presentational components
// ❌ WRONG
export function Component() {
const { user } = useAuth() // Assumes context exists
return <div>{user?.name}</div>
}
❌ Don't hardcode API endpoints
// ❌ WRONG
const response = await fetch('/api/posts/123') // Hardcoded
❌ Don't assume specific contexts or providers
// ❌ WRONG
export function Component() {
const context = useContext(SomeContext) // Assumes provider exists
}
Before (Non-Reusable):
fetch() callsuseAuth() or data hooksAfter (Reusable):
Remember: If a component can't be used in a different part of the app with different data, it's not reusable.
CRITICAL: Every component MUST pass these validation checks before being considered complete.
Before writing ANY component code, verify:
ls components/ui/grep -r "keyword" components/enterprise/, admin/, group/, etc.)useAuth, useQuery, etc.)After component is created, automatically run these checks:
# Run this validation script for every new component
COMPONENT_FILE="components/example/new-component.tsx"
echo "🔍 Validating component reusability: $COMPONENT_FILE"
# Check 1: No hardcoded fetch calls in presentational components
echo "✓ Checking for hardcoded API calls..."
if grep -n "fetch\|\.get\|\.post\|\.put\|\.delete" "$COMPONENT_FILE" | grep -v "Container\|container"; then
echo "❌ FAIL: Hardcoded API calls found. Extract to container component."
exit 1
fi
# Check 2: No direct data hooks in presentational components
echo "✓ Checking for direct data hooks..."
if grep -n "useAuth\|useQuery\|useSWR\|useContext.*User\|useContext.*Auth" "$COMPONENT_FILE" | grep -v "Container\|container"; then
echo "❌ FAIL: Direct data hooks found. Pass data via props instead."
exit 1
fi
# Check 3: Has proper TypeScript interface
echo "✓ Checking for props interface..."
if ! grep -q "interface.*Props\|type.*Props" "$COMPONENT_FILE"; then
echo "❌ FAIL: Missing props interface. Add TypeScript interface."
exit 1
fi
# Check 4: No 'any' types in props
echo "✓ Checking for type safety..."
if grep -n ":\s*any\|any\s*>" "$COMPONENT_FILE" | grep -v "//"; then
echo "⚠️ WARNING: 'any' types found. Use proper types for reusability."
fi
# Check 5: Component is exported
echo "✓ Checking component export..."
if ! grep -q "export.*function\|export.*const.*=" "$COMPONENT_FILE"; then
echo "❌ FAIL: Component not exported. Add export statement."
exit 1
fi
# Check 6: Uses established UI patterns
echo "✓ Checking for UI pattern usage..."
if grep -q "Dialog\|Sheet\|AlertDialog\|Button\|Card" "$COMPONENT_FILE"; then
echo "✅ PASS: Uses established UI patterns"
else
echo "⚠️ WARNING: Consider using established UI components"
fi
echo "✅ All validation checks passed!"
After creating component, verify:
fetch() calls - Check: grep -n "fetch" component.tsxgrep -n "useAuth\|useQuery" component.tsxgrep -A 5 "interface.*Props" component.tsxgrep -n "/api/" component.tsxany types in propsCreate a test file to verify reusability:
// components/example/new-component.test.tsx
import { render, screen } from '@testing-library/react'
import { Component } from './component'
describe('Component Reusability', () => {
it('should render with mock data', () => {
const mockData = {
id: '1',
name: 'Test',
// ... other required fields
}
render(<Component data={mockData} onAction={jest.fn()} />)
expect(screen.getByText('Test')).toBeInTheDocument()
})
it('should work without API calls', () => {
// Component should work with just props
const mockData = { /* ... */ }
render(<Component data={mockData} />)
// No network requests should be made
})
it('should accept all callbacks via props', () => {
const mockCallbacks = {
onAction: jest.fn(),
onEdit: jest.fn(),
onDelete: jest.fn(),
}
render(<Component data={mockData} {...mockCallbacks} />)
// All callbacks should be callable
expect(mockCallbacks.onAction).toBeDefined()
})
})
After validation, output:
## [COMPONENT-REUSE] Validation Results
**Component**: `components/example/new-component.tsx`
**Validation Time**: 2025-01-25 14:30:00
### ✅ Pre-Creation Validation
- [x] Component discovery completed
- [x] Existing components evaluated
- [x] Reusability design validated
- [x] Pattern compliance confirmed
### ✅ Post-Creation Validation
- [x] No hardcoded API calls
- [x] No direct data hooks
- [x] Props interface defined
- [x] Type-safe implementation
- [x] Follows established patterns
### 📋 Component Details
**Type**: Presentational Component
**Pattern**: Dialog-based
**Dependencies**: `@/components/ui/dialog`, `@/components/ui/button`
**Reusability Score**: 10/10
### ✅ VALIDATION PASSED
Component is fully reusable and ready for use anywhere in the application.
If validation fails, suggest fixes:
## ❌ [COMPONENT-REUSE] Validation FAILED
**Component**: `components/example/non-reusable.tsx`
### Issues Found:
1. ❌ Hardcoded API call on line 15: `fetch('/api/data')`
2. ❌ Direct hook usage on line 8: `const { user } = useAuth()`
3. ❌ Missing props interface
### Suggested Fixes:
#### Fix 1: Extract Data Fetching
```typescript
// Create container component
export function ComponentContainer({ id }: { id: string }) {
const [data, setData] = useState(null)
useEffect(() => {
fetch(`/api/data/${id}`).then(r => r.json()).then(setData)
}, [id])
if (!data) return <Skeleton />
return <Component data={data} />
}
// Remove useAuth(), accept user via props
interface ComponentProps {
user: User | null
data: DataType
// ...
}
Please fix the issues above and re-run validation.
### Integration with Rule 15
This skill is automatically invoked by **Rule 15: Component Reuse Enforcement** when:
- Component files are created
- Component files are modified
- User requests component creation
**Workflow:**
1. Rule 15 detects component creation request
2. Rule 15 invokes this skill (component-reuse-workflow)
3. This skill guides through discovery and validation
4. Rule 15 validates final component
5. Component is approved or fixes are required
### Continuous Validation
**Components should be re-validated when:**
- Props interface changes
- New dependencies added
- Business logic introduced
- API calls added
- Hooks added
**Re-validation command:**
```bash
# Re-validate existing component
.agent/scripts/validate-component-reusability.sh components/example/component.tsx
A component passes validation when:
Remember: Validation is not optional. Every component MUST pass before being considered complete.
tools
Webpack build optimization expert with deep knowledge of configuration patterns, bundle analysis, code splitting, module federation, performance optimization, and plugin/loader ecosystem. Use PROACTIVELY for any Webpack bundling issues including complex optimizations, build performance, custom plugins/loaders, and modern architecture patterns. If a specialized expert is a better fit, I will recommend switching and stop.
development
Web application security expert. OWASP Top 10, XSS, SQLi, CSRF, SSRF, authentication bypass, IDOR. Use for web app security testing.
testing
Vitest testing framework expert for Vite integration, Jest migration, browser mode testing, and performance optimization
tools
Vite build optimization expert with deep knowledge of ESM-first development, HMR optimization, plugin ecosystem, production builds, library mode, and SSR configuration. Use PROACTIVELY for any Vite bundling issues including dev server performance, build optimization, plugin development, and modern ESM patterns. If a specialized expert is a better fit, I will recommend switching and stop.