๐จ Refine ShadCN Theme
August 22, 2025 ยท View on GitHub
A modern, production-ready UI theme package for Refine applications built with shadcn/ui components, featuring Tailwind CSS v4, React 19, and comprehensive i18n support.
โจ Key Highlights
- ๐จ Latest Tailwind CSS v4 with OKLCH color system
- โก React 19 & TypeScript ready
- ๐ Built-in i18n support (English & Turkish)
- ๐ฏ Refine-optimized components
- ๐งฉ Shadcn/ui design system
- ๐ฑ Responsive & Accessible
- ๐ง Fully customizable
๐ Live Demos
๐ฑ Vite Example
โ๏ธ Next.js Example
๐ Documentation
๐ฆ Installation
# npm
npm install @ferdiunal/refine-shadcn
# pnpm (recommended)
pnpm add @ferdiunal/refine-shadcn
# yarn
yarn add @ferdiunal/refine-shadcn
๐ง Quick Setup
1. Import Global Styles
// In your main application file (App.tsx, index.tsx, etc.)
import "@ferdiunal/refine-shadcn/dist/globals.css";
2. Basic Usage with Refine
import { Refine } from "@refinedev/core";
import { BrowserRouter } from "react-router-dom";
import { ThemeProvider } from "@ferdiunal/refine-shadcn";
function App() {
return (
<BrowserRouter>
<ThemeProvider>
<Refine
// ... your Refine configuration
>
{/* Your application */}
</Refine>
</ThemeProvider>
</BrowserRouter>
);
}
๐งฉ Core Components
Action Buttons
All buttons include built-in authorization checks, loading states, and i18n support:
import {
CreateButton,
EditButton,
DeleteButton,
ShowButton,
ListButton
} from "@ferdiunal/refine-shadcn";
function ActionBar() {
return (
<div className="flex gap-2">
<CreateButton resource="posts" />
<EditButton resource="posts" recordItemId="1" />
<ShowButton resource="posts" recordItemId="1" />
<DeleteButton
resource="posts"
recordItemId="1"
confirmTitle="Delete Post?"
confirmDescription="This will permanently delete the post."
/>
<ListButton resource="posts" />
</div>
);
}
Framework Templates
- ๐ Vite Template - Complete Vite.js setup
- ๐ Next.js Template - Next.js App Router ready
FormField Component
Type-safe form fields with automatic validation and i18n support:
import { FormField } from "@ferdiunal/refine-shadcn";
import { Input, Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@ferdiunal/refine-shadcn/ui";
function UserForm({ control }) {
return (
<div className="space-y-4">
<FormField
control={control}
name="name"
label="Full Name"
description="Enter your full name"
>
{(field) => <Input placeholder="John Doe" {...field} />}
</FormField>
<FormField
control={control}
name="role"
label="User Role"
>
{(field) => (
<Select onValueChange={field.onChange} value={field.value}>
<SelectTrigger>
<SelectValue placeholder="Select role..." />
</SelectTrigger>
<SelectContent>
<SelectItem value="admin">Administrator</SelectItem>
<SelectItem value="user">User</SelectItem>
</SelectContent>
</Select>
)}
</FormField>
</div>
);
}
๐ Internationalization (i18n)
Built-in support for multiple languages with automatic component translation:
import { Refine } from "@refinedev/core";
import { I18nProvider, locales } from "@ferdiunal/refine-shadcn";
// Quick i18n setup
const i18nProvider = {
translate: (key: string, defaultMessage?: string) => i18n.t(key, defaultMessage),
changeLocale: (lang: string) => i18n.changeLanguage(lang),
getLocale: () => i18n.language,
};
function App() {
return (
<Refine i18nProvider={i18nProvider}>
<I18nProvider>
{/* All components now support i18n automatically */}
<CreateButton /> {/* Automatically translates to "Create" or "Oluลtur" */}
<DeleteButton /> {/* Shows localized confirmation dialogs */}
</I18nProvider>
</Refine>
);
}
Supported Languages: ๐ฌ๐ง English โข ๐น๐ท Turkish
Auto-translated Components:
- โ All action buttons (Create, Edit, Delete, etc.)
- โ Form labels and validation messages
- โ Confirmation dialogs
- โ Table headers and pagination
- โ Loading states and notifications
๐ Page Components
List Page
Feature-rich data tables with filtering, sorting, and bulk actions:
๐ Table Implementation Example
import { ListPage, Table, TableFilterProps } from "@ferdiunal/refine-shadcn";
import { AvatarImage } from "@radix-ui/react-avatar";
import { BaseRecord, HttpError, useUserFriendlyName } from "@refinedev/core";
import type { UseTableReturnType } from "@refinedev/react-table";
import { Edit, Eye, Trash2 } from "lucide-react";
import { Avatar, AvatarFallback } from "@/components/ui/avatar";
import { Checkbox } from "@/components/ui/checkbox";
const UserList = () => {
const friendly = useUserFriendlyName();
const bulkDeleteAction = (
table: UseTableReturnType<BaseRecord, HttpError>,
) => {
const label = `Delete Selected (${
table.getSelectedRowModel().rows.length
}) ${friendly(
"Row",
table.getSelectedRowModel().rows.length > 1 ? "plural" : "singular",
)}`;
return {
label,
onClick: () => {
alert("Delete Selected");
},
};
};
return (
<ListPage>
<Table enableSorting enableFilters>
<Table.Column
accessorKey="id"
id={"select"}
header={({ table }) => (
<Table.CheckAll
options={[bulkDeleteAction(table)]}
table={table}
/>
)}
cell={({ row }) => (
<Checkbox
className="translate-y-[2px]"
checked={row.getIsSelected()}
onCheckedChange={(value) =>
row.toggleSelected(!!value)
}
aria-label="Select row"
key={`checkbox-${row.original.id}`}
/>
)}
/>
<Table.Column
header={"ID"}
id="id"
accessorKey="id"
enableSorting
enableHiding
/>
<Table.Column
header={"Avatar"}
id="avatar"
accessorKey="avatar"
cell={({ row }) =>
row.original.avatar?.[0]?.url && (
<Avatar>
<AvatarImage
src={row.original.avatar[0].url}
alt={row.original.avatar[0].name}
/>
<AvatarFallback>
{row.original.firstName[0]}
{row.original.lastName[0]}
</AvatarFallback>
</Avatar>
)
}
/>
<Table.Column
header={"First Name"}
accessorKey="firstName"
id="firstName"
enableSorting
enableHiding
/>
<Table.Column
header={"Last Name"}
accessorKey="lastName"
id="lastName"
enableSorting
enableHiding
/>
<Table.Column
header={"Birthday"}
accessorKey="birthday"
id="birthday"
enableSorting
enableHiding
filter={(props: TableFilterProps) => (
<Table.Filter.DateRangePicker {...props} align="end" />
)}
/>
<Table.Column
accessorKey={"id"}
id={"actions"}
cell={({ row: { original } }) => (
<Table.Actions>
<Table.ShowAction
title="Detail"
row={original}
resource="users"
icon={<Eye size={16} />}
/>
<Table.EditAction
title="Edit"
row={original}
resource="users"
icon={<Edit size={16} />}
/>
<Table.DeleteAction
title="Delete"
row={original}
withForceDelete={true}
resource="users"
icon={<Trash2 size={16} />}
/>
</Table.Actions>
)}
/>
</Table>
</ListPage>
);
};
export default UserList;
Show Page
Clean, structured display for individual record details:
๐๏ธ Show Page Example
import { ShowPage } from "@ferdiunal/refine-shadcn";
import { IResourceComponentsProps, useShow } from "@refinedev/core";
import { IUser } from "./Form";
const UserShow: React.FC<IResourceComponentsProps> = () => {
const {
query: { data },
} = useShow<IUser>();
const record = data?.data;
return (
<ShowPage>
<ShowPage.Row title="ID" children={record?.id as number} />
<ShowPage.Row
title="First Name"
children={record?.firstName?.toString() || ""}
/>
<ShowPage.Row
title="Last Name"
children={record?.firstName?.toString() || ""}
/>
<ShowPage.Row
title="Email"
children={record?.email?.toString() || ""}
/>
</ShowPage>
);
};
export default UserShow;
Create Page
User-friendly forms for creating new records with validation:
โ Create Form Example
import { CreatePage } from "@ferdiunal/refine-shadcn";
import { Field, Form } from "@ferdiunal/refine-shadcn";
import { zodResolver } from "@hookform/resolvers/zod";
import { RedirectAction } from "@refinedev/core";
import { useForm } from "@refinedev/react-hook-form";
import * as z from "zod";
import { Input } from "@/components/ui/input";
export interface IUser {
id: number;
firstName: string;
lastName: string;
email: string;
}
const formSchema = z.object({
firstName: z.string().min(2, {
message: "Firstname must be at least 2 characters.",
}),
lastName: z.string().min(2, {
message: "Lastname must be at least 2 characters.",
}),
email: z.string().email({
message: "Please enter a valid email address.",
}),
});
const UserCreate = () => {
const { ...form } = useForm<z.infer<typeof formSchema>>({
mode: "all",
resolver: zodResolver(formSchema),
defaultValues: {
firstName: "",
lastName: "",
email: "",
},
refineCoreProps: {
autoSave: {
enabled: true,
},
redirect,
},
warnWhenUnsavedChanges: true,
});
return (
<CreatePage>
<Form {...form}>
<Field {...form} name="firstName" label="Firstname">
<Input placeholder="Firstname" />
</Field>
<Field {...form} name="lastName" label="Lastname">
<Input placeholder="Lastname" />
</Field>
<Field {...form} name="email" label="Email">
<Input placeholder="email" type="email" />
</Field>
</Form>
</CreatePage>
);
};
export default UserCreate;
Edit Page
Pre-populated forms for updating existing records:
โ๏ธ Edit Form Example
import { EditPage } from "@ferdiunal/refine-shadcn";
import { Field, Form } from "@ferdiunal/refine-shadcn";
import { zodResolver } from "@hookform/resolvers/zod";
import { RedirectAction } from "@refinedev/core";
import { useForm } from "@refinedev/react-hook-form";
import * as z from "zod";
import { Input } from "@/components/ui/input";
export interface IUser {
id: number;
firstName: string;
lastName: string;
email: string;
}
const formSchema = z.object({
firstName: z.string().min(2, {
message: "Firstname must be at least 2 characters.",
}),
lastName: z.string().min(2, {
message: "Lastname must be at least 2 characters.",
}),
email: z.string().email({
message: "Please enter a valid email address.",
}),
});
const UserEdit = () => {
const { ...form } = useForm<z.infer<typeof formSchema>>({
mode: "all",
resolver: zodResolver(formSchema),
defaultValues: {
firstName: "",
lastName: "",
email: "",
},
refineCoreProps: {
autoSave: {
enabled: true,
},
redirect,
},
warnWhenUnsavedChanges: true,
});
return (
<EditPage>
<Form {...form}>
<Field {...form} name="firstName" label="Firstname">
<Input placeholder="Firstname" />
</Field>
<Field {...form} name="lastName" label="Lastname">
<Input placeholder="Lastname" />
</Field>
<Field {...form} name="email" label="Email">
<Input placeholder="email" type="email" />
</Field>
</Form>
</EditPage>
);
};
export default UserEdit;
๐จ UI Components
Access the complete shadcn/ui component library:
import {
Button,
Input,
Card,
Dialog,
Select,
Table,
Form
} from "@ferdiunal/refine-shadcn/ui";
function MyComponent() {
return (
<Card className="p-6">
<Button variant="primary" size="lg">
Click me
</Button>
<Input placeholder="Type here..." />
</Card>
);
}
๐ฏ Advanced Features
Custom Theming
Override CSS variables to match your brand:
:root {
--primary: oklch(0.7 0.15 250);
--primary-foreground: oklch(0.98 0.02 250);
--secondary: oklch(0.96 0.01 250);
/* Customize all theme tokens */
}
TypeScript Integration
Full type safety across all components:
// Automatic type inference
type UserForm = {
name: string;
email: string;
role: 'admin' | 'user';
};
// Type-safe form fields
<FormField<UserForm, "name">
control={control}
name="name" // โ
Fully typed
label="Full Name"
>
{(field) => <Input {...field} />}
</FormField>
๐ Resources
- ๐ Documentation - Complete guide and API reference
- ๐ฎ Live Examples - Interactive demos
- ๐ Issues - Report bugs or request features
- ๐ฌ Discussions - Community support
๐ค Contributing
We welcome contributions! Please read our Contributing Guide for details on our code of conduct and the process for submitting pull requests.
๐ License
This project is licensed under the MIT License - see the LICENSE file for details.
๐ Acknowledgments
- Refine - The headless React framework
- shadcn/ui - Beautiful component library
- Tailwind CSS - Utility-first CSS framework
- Radix UI - Accessible component primitives
Made with โค๏ธ by @ferdiunal
โญ Star this repo if it helped you!