NH-UI is built with TypeScript from the ground up, providing complete type safety and excellent developer experience.
IntelliSense
Full autocomplete for all component props, including descriptions and default values.
Type Checking
Catch errors at compile time, not runtime. Invalid props are highlighted immediately.
Refactoring
Rename props, update types, and refactor with confidence using IDE tools.
import {
NHButton,
NHButtonProps,
NHCard,
NHCardProps
} from '@noiseheroes/ui';
// Using component prop types
const MyButton: React.FC<NHButtonProps> = (props) => {
return <NHButton {...props} />;
};
// Extending component props
interface CustomButtonProps extends NHButtonProps {
analytics?: {
event: string;
category: string;
};
}
const TrackedButton: React.FC<CustomButtonProps> = ({
analytics,
onClick,
...props
}) => {
const handleClick = (e: React.MouseEvent) => {
if (analytics) {
trackEvent(analytics.event, analytics.category);
}
onClick?.(e);
};
return <NHButton {...props} onClick={handleClick} />;
};// All props are fully typed with JSDoc comments
<NHButton
color="primary" // "primary" | "secondary" | "success" | "warning" | "danger"
size="lg" // "sm" | "md" | "lg"
variant="solid" // "solid" | "bordered" | "light" | "flat" | "faded" | "shadow"
isLoading={false} // boolean
isDisabled={false} // boolean
startContent={<Icon />} // React.ReactNode
endContent={<Icon />} // React.ReactNode
onPress={(e) => { // (e: PressEvent) => void
console.log('Pressed!');
}}
>
Click me
</NHButton>
// TypeScript will catch these errors:
<NHButton
color="invalid" // ❌ Type '"invalid"' is not assignable to type Color
size={123} // ❌ Type 'number' is not assignable to type Size
isLoading="false" // ❌ Type 'string' is not assignable to type boolean
/>import { NHInput, NHButton, NHSelect } from '@noiseheroes/ui';
// Input change event
<NHInput
onChange={(e: React.ChangeEvent<HTMLInputElement>) => {
const value = e.target.value; // string
console.log('Input value:', value);
}}
/>
// Button press event (HeroUI uses onPress instead of onClick)
<NHButton
onPress={(e) => {
// e is automatically typed as PressEvent
console.log('Button pressed');
}}
/>
// Select change event
<NHSelect
onSelectionChange={(key: React.Key) => {
// key is string | number
console.log('Selected:', key);
}}
>
<NHSelectItem key="option1">Option 1</NHSelectItem>
<NHSelectItem key="option2">Option 2</NHSelectItem>
</NHSelect>
// Form submission with type safety
interface FormData {
email: string;
password: string;
}
const handleSubmit = (data: FormData) => {
// data is fully typed
console.log(data.email, data.password);
};// Table with generic data types
interface User {
id: number;
name: string;
email: string;
role: 'admin' | 'user';
}
<NHTable<User>
data={users}
columns={[
{
key: 'name',
label: 'Name',
render: (user) => user.name // user is typed as User
},
{
key: 'email',
label: 'Email',
render: (user) => user.email
},
{
key: 'role',
label: 'Role',
render: (user) => (
<NHChip color={user.role === 'admin' ? 'primary' : 'default'}>
{user.role}
</NHChip>
)
}
]}
/>
// Select with typed options
interface Option {
value: string;
label: string;
icon?: React.ReactNode;
}
const options: Option[] = [
{ value: 'react', label: 'React', icon: <ReactIcon /> },
{ value: 'vue', label: 'Vue', icon: <VueIcon /> },
{ value: 'angular', label: 'Angular', icon: <AngularIcon /> }
];
<NHSelect<Option>
items={options}
onSelectionChange={(selected) => {
// selected is typed as Option
console.log(selected.value, selected.label);
}}
/>import {
Color,
Size,
Variant,
ResponsiveValue
} from '@noiseheroes/ui';
// Using theme types
const primaryColor: Color = 'primary';
const buttonSize: Size = 'md';
const cardVariant: Variant = 'bordered';
// Responsive values
const responsivePadding: ResponsiveValue<number> = {
base: 4,
sm: 6,
md: 8,
lg: 10,
xl: 12
};
// Component-specific types
import type {
NHButtonProps,
NHCardProps,
NHInputProps
} from '@noiseheroes/ui';
// Creating custom component with NH-UI types
interface CustomCardProps extends NHCardProps {
featured?: boolean;
category?: 'blog' | 'product' | 'news';
}
const CustomCard: React.FC<CustomCardProps> = ({
featured,
category,
...props
}) => {
return (
<NHCard
{...props}
className={cn(
props.className,
featured && 'border-2 border-orange-500',
category && `category-${category}`
)}
/>
);
};1. Use Type Imports
import type { NHButtonProps } from '@noiseheroes/ui';Reduces bundle size by ensuring types are removed at build time.
2. Extend Component Props
interface MyProps extends NHCardProps { custom: string; }Inherit all the original props while adding your own.
3. Use Discriminated Unions
type Status = { type: 'loading' } | { type: 'error'; message: string } | { type: 'success'; data: T };TypeScript can narrow types based on discriminator fields.
4. Avoid Type Assertions
// ❌ Avoid: value as string
// ✅ Prefer: proper typing or type guardsLet TypeScript infer types naturally for better safety.
{
"compilerOptions": {
"target": "ES2020",
"lib": ["dom", "dom.iterable", "esnext"],
"allowJs": true,
"skipLibCheck": true,
"strict": true,
"forceConsistentCasingInFileNames": true,
"noEmit": true,
"esModuleInterop": true,
"module": "esnext",
"moduleResolution": "bundler",
"resolveJsonModule": true,
"isolatedModules": true,
"jsx": "preserve",
"incremental": true,
"plugins": [
{
"name": "next"
}
],
"paths": {
"@/*": ["./src/*"],
"@noiseheroes/ui": ["./node_modules/@noiseheroes/ui"]
}
},
"include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", ".next/types/**/*.ts"],
"exclude": ["node_modules"]
}