How to Use shadcn/ui Components (Button, Input, Dialog) in refine-shadcn
Import Button, Input, Dialog, and other shadcn/ui primitives directly from @ferdiunal/refine-shadcn/ui to use them with built-in theme awareness and Refine-specific defaults.
The ferdiunal/refine-shadcn repository provides a thin, opinionated wrapper around original shadcn/ui components, enabling seamless integration with the Refine framework. These components ship with Tailwind-based styling, Radix UI primitives, and built-in support for Refine's form handling and theming systems.
Import Pattern for shadcn/ui Components
All underlying shadcn/ui primitives are re-exported through the @ferdiunal/refine-shadcn/ui namespace. The package's exports field in packages/theme/package.json maps the "./ui" entry to compiled distribution files generated from packages/theme/src/ui/index.ts.
Import components directly from the UI entry point:
import { Button, Input, Dialog } from "@ferdiunal/refine-shadcn/ui";
This single entry point exposes the full API surface of Button, Input, Dialog, and additional shadcn/ui primitives without requiring separate installations or internal path references.
Component Architecture and Implementation
Each component in packages/theme/src/ui/ implements a consistent pattern: Radix UI primitives wrapped with class-variance-authority (cva) for variant management and the cn utility function for conditional class merging.
Button Component
The Button implementation in packages/theme/src/ui/button.tsx extends the base Radix primitive with Tailwind styling and variant configurations. It supports standard shadcn/ui variants (default, destructive, outline, secondary, ghost, link) plus Refine-specific enhancements for loading states and disabled handling.
Key implementation details:
- Uses
cvato generate type-safe variant classes - Forwards refs and all native button attributes
- Integrates with the
ThemeProviderfrompackages/theme/src/providers/theme-provider.tsx
Input Component
Located at packages/theme/src/ui/input.tsx, the Input wrapper provides a thin abstraction over the native HTML <input> element. It forwards all standard attributes while applying consistent styling for focus states, borders, and background colors through the Tailwind configuration.
Dialog Component
The Dialog family in packages/theme/src/ui/dialog.tsx implements a full modal system using @radix-ui/react-dialog. This includes DialogTrigger, DialogContent, DialogHeader, DialogTitle, DialogDescription, DialogFooter, and DialogClose components.
The implementation adds:
- Built-in animations via Tailwind transitions
- Optional close button visibility via the
showCloseButtonprop - Backdrop styling consistent with the theme provider
Practical Usage Examples
Basic Button with Variants
Create styled buttons using the variant API defined in the source:
import { Button } from "@ferdiunal/refine-shadcn/ui";
export const ActionButton = () => (
<Button
variant="default"
size="lg"
onClick={() => console.log("Action triggered")}
>
Execute Action
</Button>
);
The variant prop accepts values mapped through the cva configuration in button.tsx, while size controls padding and height dimensions.
Input Integration with Refine Forms
Combine Input with Refine's FormField for type-safe form handling:
import { Input } from "@ferdiunal/refine-shadcn/ui";
import { FormField } from "@ferdiunal/refine-shadcn";
export const EmailField = ({ control }) => (
<FormField
control={control}
name="email"
label="Email Address"
description="Enter your business email"
>
{(field) => (
<Input
type="email"
placeholder="user@company.com"
{...field}
/>
)}
</FormField>
);
The Input component forwards all React input props, ensuring compatibility with React Hook Form's field handlers.
Complete Dialog Implementation
Build accessible modals using the full Dialog compound component pattern:
import {
Dialog,
DialogTrigger,
DialogContent,
DialogHeader,
DialogTitle,
DialogDescription,
DialogFooter,
DialogClose,
} from "@ferdiunal/refine-shadcn/ui";
export const ConfirmDialog = () => (
<Dialog>
<DialogTrigger asChild>
<Button variant="outline">Delete Item</Button>
</DialogTrigger>
<DialogContent showCloseButton>
<DialogHeader>
<DialogTitle>Confirm Deletion</DialogTitle>
<DialogDescription>
This action cannot be undone. The item will be permanently removed from your account.
</DialogDescription>
</DialogHeader>
<DialogFooter>
<DialogClose asChild>
<Button variant="secondary">Cancel</Button>
</DialogClose>
<Button variant="destructive" onClick={handleDelete}>
Delete
</Button>
</DialogFooter>
</DialogContent>
</Dialog>
);
The showCloseButton prop toggles the visibility of the upper-right close control, while asChild on triggers and closes ensures proper composition with button elements.
Combined Example in a Refine Action
Use all three components together in a create or edit form wrapped in a dialog:
import { Button, Input, Dialog } from "@ferdiunal/refine-shadcn/ui";
import { FormField } from "@ferdiunal/refine-shadcn";
import { useForm } from "@refinedev/react-hook-form";
export const CreateRecordModal = () => {
const { control, handleSubmit } = useForm();
const onSubmit = (data) => console.log(data);
return (
<Dialog>
<DialogTrigger asChild>
<Button>Create New Record</Button>
</DialogTrigger>
<DialogContent>
<form onSubmit={handleSubmit(onSubmit)} className="space-y-4">
<FormField
control={control}
name="title"
label="Record Title"
>
{(field) => <Input placeholder="Enter title..." {...field} />}
</FormField>
<FormField
control={control}
name="identifier"
label="Identifier"
>
{(field) => <Input placeholder="ID-123" {...field} />}
</FormField>
<div className="flex justify-end gap-2 pt-4">
<DialogClose asChild>
<Button variant="outline">Cancel</Button>
</DialogClose>
<Button type="submit">Save Record</Button>
</div>
</form>
</DialogContent>
</Dialog>
);
};
Summary
- Import from
@ferdiunal/refine-shadcn/uito access all shadcn/ui primitives compiled frompackages/theme/src/ui/ - Components wrap Radix UI primitives with Tailwind styling via
cvaand thecnutility - Source files for Button, Input, and Dialog live in
packages/theme/src/ui/button.tsx,input.tsx, anddialog.tsxrespectively - Theme awareness is built-in through integration with the
ThemeProvider - Refine compatibility includes loading states, form field forwarding, and disabled handling patterns
Frequently Asked Questions
Do I need to install shadcn/ui separately when using refine-shadcn?
No, all shadcn/ui components are bundled within the @ferdiunal/refine-shadcn package. The components are compiled by tsup from packages/theme/src/ui/ into the distribution folder and exposed through the "./ui" export in packages/theme/package.json. Importing from @ferdiunal/refine-shadcn/ui provides the same API without additional dependencies.
Can I customize the Tailwind classes for these components?
Yes, all components accept a className prop that gets merged with base styles through the cn utility function. The source files use class-variance-authority (cva) for variant definitions, allowing you to override or extend the default Tailwind classes. You can also modify the base components directly in packages/theme/src/ui/ before compilation.
Are Radix UI primitives exposed directly?
The underlying Radix UI primitives (@radix-ui/react-dialog, @radix-ui/react-slot, etc.) are used internally but not re-exported directly. Instead, they are wrapped with Refine-specific logic and Tailwind styling. If you need direct access to Radix primitives, you would install them separately, though the wrapped versions in refine-shadcn provide all standard Radix functionality plus theme integration.
How does theme switching work with these components?
Components automatically respond to the ThemeProvider defined in packages/theme/src/providers/theme-provider.tsx. The provider manages dark/light mode state and CSS variable injection. Because components use Tailwind's dark mode modifiers and the cn utility for class merging, they react dynamically to theme changes without requiring manual configuration per component.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →