Guide for creating React components using HeroUI in the DEVS platform. Use this when asked to create new UI components, pages, or modify existing React code.
When creating React components in the DEVS platform, follow these patterns using HeroUI and project conventions.
Components are organized as:
src/components/ - Shared/reusable componentssrc/pages/ - Route-based page componentssrc/features/{feature}/components/ - Feature-specific componentsimport { useState, useCallback } from 'react'
import { Button, Card, CardBody, Spinner } from '@heroui/react'
import { useTranslation } from 'react-i18next'
import { Icon } from '@/components/Icon'
interface MyComponentProps {
title: string
onAction?: () => void
isDisabled?: boolean
children?: React.ReactNode
}
export function MyComponent({
title,
onAction,
isDisabled = false,
children,
}: MyComponentProps) {
const { t } = useTranslation()
const [isLoading, setIsLoading] = useState(false)
const handleAction = useCallback(async () => {
if (!onAction) return
setIsLoading(true)
try {
await onAction()
} finally {
setIsLoading(false)
}
}, [onAction])
return (
<Card>
<CardBody className="gap-4">
<h2 className="text-lg font-semibold">{title}</h2>
{children}
<Button
color="primary"
isDisabled={isDisabled || isLoading}
isLoading={isLoading}
onPress={handleAction}
>
{t('common.submit')}
</Button>
</CardBody>
</Card>
)
}
Always import from @heroui/react:
import {
Button,
Card,
CardBody,
CardHeader,
CardFooter,
Modal,
ModalContent,
ModalHeader,
ModalBody,
ModalFooter,
Input,
Textarea,
Select,
SelectItem,
Chip,
Badge,
Avatar,
Spinner,
Progress,
Tooltip,
Popover,
PopoverTrigger,
PopoverContent,
Dropdown,
DropdownTrigger,
DropdownMenu,
DropdownItem,
Table,
TableHeader,
TableBody,
TableColumn,
TableRow,
TableCell,
Tabs,
Tab,
Accordion,
AccordionItem,
Divider,
Switch,
Checkbox,
} from '@heroui/react'
All user-facing strings must be translatable:
import { useTranslation } from 'react-i18next'
function MyComponent() {
const { t } = useTranslation()
return <Button>{t('agents.create')}</Button>
}
Use curly apostrophe ' (not ') in translation strings.
Always use @/ prefix for imports:
// ✅ Correct
import { Agent } from '@/types'
import { Icon } from '@/components/Icon'
// ❌ Wrong
import { Agent } from '../../types'
Use HeroUI's onPress instead of onClick for buttons:
// ✅ Correct
<Button onPress={handleClick}>Click me</Button>
// ❌ Avoid for HeroUI buttons
<Button onClick={handleClick}>Click me</Button>
Use HeroUI's built-in loading props:
<Button isLoading={isLoading} isDisabled={isLoading}>
Submit
</Button>
<Spinner size="sm" />
Use the project's Icon component with Lucide icons:
import { Icon } from '@/components/Icon'
<Icon name="Plus" size={20} />
<Icon name="Settings" className="text-default-500" />
Use Tailwind CSS classes. Common patterns:
// Flex layouts
<div className="flex items-center gap-2">
<div className="flex flex-col gap-4">
// Spacing
<div className="p-4 m-2">
<div className="px-4 py-2">
// Text styling
<span className="text-sm text-default-500">
<h2 className="text-lg font-semibold">
Use Zustand stores with selectors for performance:
import { useAgentStore } from '@/stores/agentStore'
function AgentList() {
// ✅ Good - select only what you need
const agents = useAgentStore((state) => state.agents)
const isLoading = useAgentStore((state) => state.isLoading)
// ❌ Bad - subscribes to entire store
const store = useAgentStore()
}
import {
Modal,
ModalContent,
ModalHeader,
ModalBody,
ModalFooter,
Button,
useDisclosure,
} from '@heroui/react'
function MyModal() {
const { isOpen, onOpen, onOpenChange } = useDisclosure()
return (
<>
<Button onPress={onOpen}>Open Modal</Button>
<Modal isOpen={isOpen} onOpenChange={onOpenChange}>
<ModalContent>
{(onClose) => (
<>
<ModalHeader>Title</ModalHeader>
<ModalBody>Content</ModalBody>
<ModalFooter>
<Button variant="light" onPress={onClose}>
Cancel
</Button>
<Button color="primary" onPress={onClose}>
Confirm
</Button>
</ModalFooter>
</>
)}
</ModalContent>
</Modal>
</>
)
}
Component tests go in src/test/components/:
import { render, screen } from '@testing-library/react'
import { describe, it, expect } from 'vitest'
import { MyComponent } from '@/components/MyComponent'
describe('MyComponent', () => {
it('renders title correctly', () => {
render(<MyComponent title="Test Title" />)
expect(screen.getByText('Test Title')).toBeInTheDocument()
})
})
npx skills add codename-co/react-component-development下载完整 Skill 目录,包含 SKILL.md 及所有相关文件
Search for places (restaurants, cafes, etc.) via Google Places API proxy on localhost.
Interact with GitHub using the `gh` CLI. Use `gh issue`, `gh pr`, `gh run`, and `gh api` for issues, PRs, CI runs, and advanced queries.
Create or update AgentSkills. Use when designing, structuring, or packaging skills with scripts, references, and assets.
Start voice calls via the OpenClaw voice-call plugin.
Notion API for creating and managing pages, databases, and blocks.
Gemini CLI for one-shot Q&A, summaries, and generation.
Category:developer