@undefine-ui/design-system
v3.2.0
Published
Define design system React components and theme.
Downloads
2,968
Readme
@undefine-ui/design-system
React component library + MUI theme layer extracted from the Define design system.
Installation
pnpm add @undefine-ui/design-system
# peer deps you will already have in most React + MUI apps
pnpm add @mui/material @mui/x-data-grid @emotion/react @emotion/styled react react-dom react-hook-formNote: Fonts (Work Sans and Geist) are bundled with the package and automatically loaded by the ThemeProvider. You don't need to install or import @fontsource packages separately.
Usage
Providers
Every consumer app needs the same provider stack that the showcase uses:
import { SettingsProvider, defaultSettings, ThemeProvider } from '@undefine-ui/design-system';
export function DesignSystemApp({ children }: { children: React.ReactNode }) {
return (
<SettingsProvider settings={defaultSettings}>
<ThemeProvider>{children}</ThemeProvider>
</SettingsProvider>
);
}SettingsProviderexposes the design-system preferences (mode, contrast, fonts, nav layout) through theuseSettingshook. Provide your ownsettingsobject if you want different defaults or if you persist user choices.ThemeProviderwraps MUI's CssVarsProvider with the Define theme (createTheme). It accepts any React children and automatically injectsCssBaselineand loads the required fonts (Work Sans and Geist).- Both providers are exported from the package root so you can colocate them with your router/root layout.
Theming hooks
If you need to read or update theme settings at runtime:
import { useSettings } from '@undefine-ui/design-system';
const ThemeSwitcher = () => {
const { colorScheme, onUpdateField } = useSettings();
return (
<ToggleButtonGroup
value={colorScheme}
onChange={(_, value) => value && onUpdateField('colorScheme', value)}
>
{/* buttons */}
</ToggleButtonGroup>
);
};useSettings is a thin wrapper around React context, so it must be used inside SettingsProvider.
Component imports
import {
CopyButton,
Field,
Form,
Icon,
Logo,
LoadingScreen,
OTPInput,
Table,
Upload
} from '@undefine-ui/design-system';Storybook (pnpm dev:storybook) documents each component and token surface.
Icon library
The package includes a comprehensive icon library with 45+ SVG icons organized by category.
Available icons:
- Navigation: NavArrowLeft, NavArrowRight, NavArrowDown, NavArrowDownSolid, LongArrowUpLeftSolid
- User: User, UserSolid, Building, Bank
- Form Controls: CheckboxDefault, CheckboxSelect, CheckboxIndeterminate, RadioDefault, RadioSelect
- Actions: Search, Copy, Trash, XMark, XMarkSolid, CloudUpload, Download, Settings, Plus, PlusSquare, Attachment
- Feedback: InfoCircle, InfoCircleSolid, CheckCircleSolid, HelpCircle, ClipboardCheck, Loader, BellNotification, Circle
- Visibility: Eye, EyeClosed
- Date & Time: Calendar, Clock
- Data: SortUp, SortDown, StatUp, StatDown
- Toast: InfoToast, SuccessToast, WarningToast, ErrorToast
- System: KeyCommand
Usage:
import { Icon } from '@undefine-ui/design-system';
// Basic usage
<Icon icon="Search" sx={{ width: 24, height: 24 }} />
// With custom color
<Icon icon="InfoCircleSolid" sx={{ width: 32, height: 32, color: 'primary.main' }} />
// Different sizes
<Icon icon="Loader" sx={{ width: 16, height: 16 }} />
<Icon icon="Loader" sx={{ width: 48, height: 48 }} />Props:
icon- Icon name (required, type:IconType)sx- MUI sx prop for styling (size, color, etc.)- All
BoxPropsfrom MUI are supported
The Icon component renders SVG icons with full theme integration and accepts any MUI Box props for flexible styling.
Image
A high-performance lazy-loading image component with responsive image support, fallback handling, and smooth loading transitions. Perfect for optimizing image delivery across different devices and screen sizes.
Usage:
import { Image } from '@undefine-ui/design-system';
// Basic usage with lazy loading
<Image
src="/path/to/image.jpg"
alt="Description"
aspectRatio="16 / 9"
/>
// Responsive images with srcSet
<Image
src="/image-1280w.jpg"
srcSet="/image-320w.jpg 320w, /image-640w.jpg 640w, /image-1280w.jpg 1280w"
sizes="(max-width: 600px) 100vw, (max-width: 1200px) 50vw, 33vw"
alt="Responsive image"
aspectRatio="16 / 9"
/>
// Retina display support
<Image
src="/image.jpg"
srcSet="/image.jpg 1x, /[email protected] 2x, /[email protected] 3x"
alt="High DPI image"
/>
// With fallback for error handling
<Image
src="/primary-image.jpg"
fallbackSrc="/fallback-image.jpg"
alt="Image with fallback"
aspectRatio="4 / 3"
/>
// Disable lazy loading for above-the-fold images
<Image
src="/hero-image.jpg"
alt="Hero image"
lazy={false}
aspectRatio="21 / 9"
/>
// Custom loading indicator
<Image
src="/image.jpg"
alt="Custom loading"
loadingIndicator={<CircularProgress />}
/>
// With overlay content
<Image
src="/image.jpg"
alt="Image with overlay"
overlay={
<Box sx={{ p: 2, color: 'white' }}>
<Typography variant="h5">Overlay Text</Typography>
</Box>
}
/>
// Custom object-fit and position
<Image
src="/portrait.jpg"
alt="Portrait"
aspectRatio="1"
fit="contain"
position="top center"
/>Props:
src- Image source URL (required)alt- Alternative text for accessibility (required)lazy- Enable lazy loading with Intersection Observer (default:true)srcSet- Responsive image sources for different screen sizes/resolutionssizes- Defines which image size to use at different viewport widthsfallbackSrc- Fallback URL when the main source fails to loadaspectRatio- Aspect ratio to prevent layout shift (e.g.,"16 / 9",1.5)fit- Controlsobject-fitCSS property (default:"cover")position- Controlsobject-positionCSS property (default:"center")overlay- Optional overlay content rendered above the imagewithOverlay- Enables overlay even without contentloadingIndicator- Custom loading indicator componentrenderError- Custom error fallback UIobserverMargin- Intersection Observer root margin (default:"200px")imgSx- Additional styles for the image elementimgProps- Additional props for the underlying image elementonLoad- Callback fired when the image loadsonError- Callback fired when the image fails to load
Features:
- Lazy loading: Uses Intersection Observer for efficient viewport detection
- Responsive images: Full support for
srcSetandsizesattributes - Fallback handling: Automatic retry with fallback source on error
- Layout stability: Prevents layout shift with aspect ratio control
- Smooth transitions: Fade-in animation when image loads
- Custom loading states: Skeleton loader by default, customizable
- Error handling: Graceful error UI with customization options
- Overlay support: Render content on top of images
- Browser fallback: Uses native
loading="lazy"when IntersectionObserver unavailable - Flexible styling: Supports all MUI Box props for container and image
Performance Tips:
- Use
lazy={false}for above-the-fold images to avoid lazy loading delay - Always specify
aspectRatioto prevent layout shift during loading - Use
srcSetwith appropriate sizes for optimal image delivery - Set
observerMargin="400px"to preload images slightly before viewport
Toast Notifications
Toast notifications powered by Sonner with Define styling. Supports success, error, warning, info variants with optional descriptions and action buttons.
Setup:
Add the Toast component once at the root of your application:
import {
SettingsProvider,
defaultSettings,
ThemeProvider,
Toast
} from '@undefine-ui/design-system';
export function App({ children }: { children: React.ReactNode }) {
return (
<SettingsProvider settings={defaultSettings}>
<ThemeProvider>
<Toast />
{children}
</ThemeProvider>
</SettingsProvider>
);
}Usage:
import { toast } from '@undefine-ui/design-system';
// Basic toast
toast('This is a basic toast');
// Variants
toast.success('Success Notification');
toast.error('Error Notification');
toast.warning('Warning Notification');
toast.info('Information Notification');
toast.loading('Loading...');
// With description
toast.success('Event created', {
description: 'Your event has been created successfully'
});
// With action buttons
toast('Are you sure?', {
description: 'This action cannot be undone',
cancel: {
label: 'Cancel',
onClick: () => console.log('Cancelled')
},
action: {
label: 'Confirm',
onClick: () => console.log('Confirmed')
}
});
// Promise-based (auto shows loading → success/error)
toast.promise(fetchData(), {
loading: 'Saving changes...',
success: 'Changes saved successfully',
error: 'Failed to save changes'
});Features:
- Multiple variants: Success, error, warning, info, loading states
- Action buttons: Cancel and action buttons with callbacks
- Promise support: Auto-transition from loading to success/error
- Dark themed: Consistent dark background with themed icons
- Positioned: Top-right with expandable queue
Date Pickers (React Hook Form)
Form-ready date picker components integrated with React Hook Form. Includes RHFDatePicker, RHFTimePicker, and RHFDateTimePicker for seamless form state management.
Usage:
import { Form, RHFDatePicker, RHFTimePicker, RHFDateTimePicker } from '@undefine-ui/design-system';
import { useForm } from 'react-hook-form';
const MyForm = () => {
const methods = useForm({
defaultValues: {
birthDate: null,
meetingTime: null,
eventStart: null
}
});
return (
<Form methods={methods} onSubmit={(data) => console.log(data)}>
{/* Date Picker */}
<RHFDatePicker name="birthDate" label="Birth Date" />
{/* Time Picker */}
<RHFTimePicker name="meetingTime" label="Meeting Time" />
{/* DateTime Picker */}
<RHFDateTimePicker name="eventStart" label="Event Start" />
{/* Clearable variant */}
<RHFDatePicker name="deadline" label="Deadline" clearable />
</Form>
);
};Props (shared by all variants):
name- Form field name for react-hook-form (required)label- Label for the picker inputhelperText- Helper text displayed below the inputclearable- Enable clear button (default:false)disabled- Disable the picker (default:false)- All MUI X DatePicker/TimePicker/DateTimePicker props are supported
Features:
- Form integration: Automatically syncs with react-hook-form state
- Validation: Displays validation errors from react-hook-form
- Clearable: Optional clear button to reset the value
- Theme-styled: Uses your theme's TextField styling
- Click-to-open: Opens picker on text field click
OTP Input
A one-time password input component with keyboard navigation, paste support, and validation. Perfect for email/SMS verification, PIN codes, and security codes.
Usage:
import { OTPInput, Field } from '@undefine-ui/design-system';
// Basic usage
<OTPInput
length={6}
onChange={(otp) => console.log(otp)}
onComplete={(otp) => console.log('Complete:', otp)}
helperText="Enter the 6-digit code"
/>
// With React Hook Form
<Field.OTP
name="verificationCode"
length={6}
helperText="Enter the code sent to your email"
/>
// 4-digit PIN
<Field.OTP
name="pin"
length={4}
helperText="Enter your PIN"
/>
// Error state
<OTPInput
length={6}
error
helperText="Invalid code. Please try again."
/>Props:
length- Number of OTP input fields (default:6)onChange- Callback fired when OTP value changes(otp: string) => voidonComplete- Callback fired when all fields are filled(otp: string) => voiderror- Show error state (default:false)helperText- Helper text displayed below the inputcontainerProps- Props passed to the container Box component
Features:
- Keyboard navigation: Arrow keys to move between fields, Backspace to clear
- Paste support: Automatically fills all fields from clipboard
- Auto-focus: Moves to next field after entering a digit
- Validation: Only accepts numeric input
- Error handling: Visual error states with helper text
- Responsive: Adapts input size on mobile devices
Hook Form Integration:
The Field.OTP component automatically integrates with React Hook Form, providing validation and error handling out of the box.
Empty Content
A flexible empty state component for displaying placeholder content when data is unavailable. Perfect for empty lists, search results, or any state where content hasn't been loaded yet.
Usage:
import { EmptyContent } from '@undefine-ui/design-system';
// Basic usage
<EmptyContent title="No data" />
// With description
<EmptyContent
title="No results found"
description="Try adjusting your search or filter to find what you're looking for."
/>
// With image
<EmptyContent
title="No notifications"
description="You're all caught up!"
imgUrl="/assets/icons/empty/ic-notification.svg"
/>
// Filled variant (with background)
<EmptyContent
filled
title="No items in cart"
description="Add items to your cart to see them here."
/>
// With action button
<EmptyContent
title="No projects yet"
description="Create your first project to get started."
action={
<Button variant="contained">Create Project</Button>
}
/>
// Custom styling with slotProps
<EmptyContent
title="Custom Styled"
description="Customize title and description styles."
slotProps={{
title: { color: 'primary.main', fontWeight: 700 },
description: { color: 'text.secondary', maxWidth: 300 }
}}
/>Props:
title- Title text to display (default:'No data')description- Description text below the titleimgUrl- URL for an optional image to displayfilled- Show filled background variant with border (default:false)action- Optional action element (button, link, etc.)slotProps- Custom styles forimg,title, anddescriptionslotssx- MUI sx prop for container styling- All
StackPropsfrom MUI are supported
Features:
- Flexible layout: Centers content vertically and horizontally
- Filled variant: Adds background color and dashed border
- Image support: Display custom illustrations or icons
- Action slot: Add buttons or links for user interaction
- Customizable: Style individual elements via slotProps
- Theme integration: Uses theme variables for consistent styling
Logo assets
The package exports two Logo components:
<Logo />
Renders logo images hosted on Cloudinary by default. The component automatically selects the appropriate variant based on props:
| Variant flag combo | Asset served |
| -------------------------- | ----------------------------------- |
| isFull={false} (default) | Single logo (60px width) |
| isFull | Full logo with text (120px width) |
| isWhite | White variant for dark backgrounds |
| isBlack | Black variant for light backgrounds |
| isWhite + isFull | White full logo |
| isBlack + isFull | Black full logo |
All logo assets are served from Cloudinary and don't require any local files in your host app.
<Logo isFull isWhite href="/dashboard" />Props:
isFull- Use full logo with text (default:false)isWhite- Use white variant (default:false)isBlack- Use black variant (default:false)disableLink- Render without link wrapper (default:false)href- Link destination (default:'/')LinkComponent- Custom link component (default:'a')src- Override logo source URLalt- Image alt text (default:'Undefine UI logo')sx- MUI sx prop for styling
<AnimatedLogo />
An animated SVG version of the logo perfect for splash screens and loading states. Features a smooth infinite animation sequence with staggered timing for visual interest.
import { AnimatedLogo } from '@undefine-ui/design-system';
<AnimatedLogo />;The animated logo automatically plays on mount with:
- Background fade-in
- Left bars sliding in from the left
- D letter scaling in with a bounce effect
- Continuous infinite loop
Ideal for splash screens, loading overlays, or brand storytelling moments.
Export surface
Everything is re-exported from src/index.ts. Key groups:
| Group | Exports |
| ---------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| Components | CopyButton, EmptyContent, HookForm helpers (Form, Field, RHFSwitch, etc.), Icon, Image, Logo, LoadingScreen, OTPInput, Table, Upload |
| Hooks | useBoolean, useCopyToClipboard, useEventListener, useLocalStorage, useResponsive, useSettings, useSetState, useScrollOffsetTop, usePopover, useCountdown |
| Contexts | SettingsProvider, SettingsContext, defaultSettings, SettingsValueProps |
| Theme | ThemeProvider, createTheme, colorSchemes, components, palette, radius, shadows, customSpacing, utilities such as varAlpha, bgGradient, hideScrollX/Y |
| Utilities | changeCase helpers, formatNumber, fullname-utils, generic helpers |
You can also import the theme pieces directly to compose your own MUI theme or to extend tokens in Storybook.
Customising the theme
- Call
createTheme(settings)to get the configuredThemeobject (useful for tests or SSR). - Theme tokens live under
src/theme/core. If you need to override palette/typography/etc., spread the exports into your ownextendThemecall. updateCoreWithSettingsandupdateComponentsWithSettingsare exported for advanced scenarios (e.g., you want to override the default settings object before rendering).
Scripts
pnpm build– bundle ESM/CJS output +.d.tsintodist/pnpm storybook– run Storybook locallypnpm build-storybook– static Storybook outputpnpm test– run Vitest (once specs are added)
