Welcome to Canary UI
A comprehensive React component library for building high-fidelity prototypes. All components match the Canary design system for consistent, professional interfaces.
Foundation Project
This is a template repository. Clone it to start building your prototype with pre-built Canary UI components.
Quick Start
tsx
import { CanaryButton, ButtonType } from "@canary-ui/components";
export default function MyComponent() {
return (
<CanaryButton type={ButtonType.PRIMARY}>
Click me!
</CanaryButton>
);
}New in v0.6.0
CanaryBadge
3 newUrgentPendingLarge badge
tsx
<CanaryBadge type={BadgeType.INFO} label="3 new" />
<CanaryBadge type={BadgeType.URGENT} label="Urgent" />
<CanaryBadge type={BadgeType.WARNING} label="Pending" />CanaryDivider
Content above
Content below
OR
Content after labeled divider
LeftRight
tsx
<CanaryDivider />
<CanaryDivider text="OR" />
<CanaryDivider direction={DividerDirection.VERTICAL} />CanaryNote
Your changes have been saved successfully.
This action will affect all properties in your group.
This guest has an outstanding balance of $245.00.
tsx
<CanaryNote color={NoteColor.OK}>Saved successfully.</CanaryNote>
<CanaryNote color={NoteColor.WARNING}>Affects all properties.</CanaryNote>
<CanaryNote color={NoteColor.DANGER}>Outstanding balance.</CanaryNote>CanaryTimestamp
DateTime:
With timezone:
Relative:
Secondary:
tsx
<CanaryTimestamp date={new Date()} format={TimestampFormat.DATETIME} />
<CanaryTimestamp date={new Date()} format={TimestampFormat.RELATIVE_TIME} />
<CanaryTimestamp date={new Date()} color={TimestampColor.SECONDARY} />CanaryFormLabel + CanaryValidationError
Email address is required
Phone number recommended for guest communication
tsx
<CanaryFormLabel label="Email Address" isRequired />
<CanaryValidationError error="Email address is required" />
<CanaryValidationError warning="Phone recommended" />CanaryProfileImage
JD
TW
AB
KL
MN
tsx
<CanaryProfileImage size={ProfileImageSize.TINY} initials="JD" />
<CanaryProfileImage size={ProfileImageSize.SMALL} initials="TW" />
<CanaryProfileImage size={ProfileImageSize.LARGE} initials="MN" />CanaryTooltip + CanaryTooltipIcon
Hover me (top)
This is a tooltip
Hover me (bottom)
Guest ID Bottom tooltip
Unique identifier assigned at check-in
Action cannot be undone
tsx
<CanaryTooltip content="This is a tooltip" position={TooltipPosition.TOP}>
<span>Hover me</span>
</CanaryTooltip>
<CanaryTooltipIcon content="Help text" color={TooltipIconColor.PRIMARY} />CanaryExpand
Room Details
Guest Preferences
Late checkout requested. Extra pillows. Non-smoking room.
tsx
const [expanded, setExpanded] = useState(false);
<CanaryExpand
isExpanded={expanded}
onToggle={() => setExpanded(!expanded)}
header={<span>Room Details</span>}
>
<p>Room 301 - Deluxe King Suite</p>
</CanaryExpand>CanaryCounter
Guests:
2
Nights:
1
tsx
const [count, setCount] = useState(1);
<CanaryCounter value={count} onChange={setCount} minValue={0} maxValue={10} />CanaryProgressBar
Continuous (65%)
65%
Steps (2 of 4)
tsx
<CanaryProgressBar variant={ProgressBarVariant.CONTINUOUS} progress={65} showLabel />
<CanaryProgressBar variant={ProgressBarVariant.STEPS} totalSteps={4} currentStep={2} />CanarySteps
Horizontal
Guest Info
2
Room Selection
3
Payment
4
Confirmation
Vertical
Check-in
Completed at 2:30 PM
2
ID Verification
In progress
3
Payment Auth
Pending
tsx
<CanarySteps
steps={[
{ label: "Guest Info" },
{ label: "Room Selection" },
{ label: "Payment" },
{ label: "Confirmation" },
]}
currentStep={1}
/>CanaryOverflowMenu
Click the three dots
tsx
<CanaryOverflowMenu
items={[
{ id: "edit", label: "Edit guest", onClick: () => {} },
{ id: "message", label: "Send message", onClick: () => {} },
{ id: "divider", label: "", isDivider: true },
{ id: "delete", label: "Remove guest", isDanger: true },
]}
/>CanaryDialog
tsx
const [open, setOpen] = useState(false);
<CanaryButton onClick={() => setOpen(true)}>Open Dialog</CanaryButton>
<CanaryDialog
isOpen={open}
onClose={() => setOpen(false)}
title="Confirm Check-out"
stretch={DialogStretch.NORMAL}
footer={<CanaryButton onClick={() => setOpen(false)}>Confirm</CanaryButton>}
>
<p>Are you sure you want to check out Room 301?</p>
</CanaryDialog>CanarySideSheet
tsx
const [open, setOpen] = useState(false);
<CanaryButton onClick={() => setOpen(true)}>Open Side Sheet</CanaryButton>
<CanarySideSheet
isOpen={open}
onClose={() => setOpen(false)}
size="medium"
header={<h3>Guest Details</h3>}
>
<p>Side sheet content here</p>
</CanarySideSheet>CanarySettingsCard
Property Information
Basic hotel details and contact information
Name: Hotel Canary
Address: 123 Main Street, New York, NY
Phone: +1 (555) 123-4567
tsx
const [state, setState] = useState(SettingsCardState.VIEW);
<CanarySettingsCard
title="Property Information"
subtitle="Basic hotel details"
state={state}
onStateChange={setState}
onSave={() => alert('Saved!')}
>
{state === SettingsCardState.VIEW
? <p>Hotel Canary - 123 Main St</p>
: <CanaryInput label="Hotel Name" value="Hotel Canary" />}
</CanarySettingsCard>CanaryAutocomplete
Select Country
tsx
const [value, setValue] = useState("");
<CanaryAutocomplete
label="Select Country"
value={value}
onChange={setValue}
options={[
{ value: "us", label: "United States" },
{ value: "ca", label: "Canada" },
{ value: "uk", label: "United Kingdom" },
]}
placeholder="Type to search..."
/>Canary UI Component Library - Built with Next.js, React, and Tailwind CSS
Clone this project to start building your prototype