Introduction
Everything you need to build with med-ui from an empty folder: framework, fonts, theme, dark mode, and your first components.
What is med-ui?
med-ui is a component library you install one component at a time. It is not an npm package you import. Each component's source code is copied into your project, so you can read it, change it, and ship it as your own. It currently has 62 components, built with React, Base UI primitives and Tailwind CSS v4.
Quick Start
Already set up (steps 1 to 5 below)? Install every component with one command:
pnpm dlx shadcn@4.21.1 add @med-ui/allRequirements
- Node.js 20 or newer
- React 19
- Tailwind CSS v4
- TypeScript (recommended; every component is written in TSX)
1. Create a Project
med-ui works with any React framework that uses Tailwind CSS v4. Pick yours, and every step below updates with the right files and paths.
Showing Next.js · change framework
Routing, server rendering and SEO out of the box. The best default for most apps.
npx create-next-app@latest my-app --src-dir --yes
cd my-app2. Dependencies
Install the packages every component relies on: the class-name helper, the animation utilities and the icon set.
npm install cn tw-animate-css lucide-reactEach component brings its own extra packages when you add it, so this is all you need up front.
3. Fonts
med-ui uses Inter for interface text, Source Serif 4 for headings and JetBrains Mono for code. All three are free.
Showing Next.js · change framework
Load them with next/font in your root layout:
import { Inter, JetBrains_Mono, Source_Serif_4 } from "next/font/google"
import "./globals.css"
const sans = Inter({ variable: "--font-sans", subsets: ["latin"] })
const serif = Source_Serif_4({ variable: "--font-serif", subsets: ["latin"] })
const mono = JetBrains_Mono({ variable: "--font-mono", subsets: ["latin"] })
export default function RootLayout({ children }: { children: React.ReactNode }) {
return (
<html lang="en" className={`${sans.variable} ${serif.variable} ${mono.variable}`}>
<body>{children}</body>
</html>
)
}4. Theme
The theme has two parts: a base stylesheet with the animations and variants the components use, and your color tokens. Download the base stylesheet next to your CSS file:
Showing Next.js · change framework
curl -o src/app/base.css https://ui.elhcn.com/base.cssThen replace the contents of src/app/globals.css with the full med-ui theme below.
curl.exe instead of curl.This is the exact theme this site uses:
@import "tailwindcss";
@import "tw-animate-css";
@import "./base.css";
@custom-variant dark (&:is(.dark *));
@theme inline {
--color-background: var(--background);
--color-foreground: var(--foreground);
--font-sans: var(--font-sans);
--font-mono: var(--font-mono);
--font-heading: var(--font-serif);
--font-serif: var(--font-serif);
--color-sidebar-ring: var(--sidebar-ring);
--color-sidebar-border: var(--sidebar-border);
--color-sidebar-accent-foreground: var(--sidebar-accent-foreground);
--color-sidebar-accent: var(--sidebar-accent);
--color-sidebar-primary-foreground: var(--sidebar-primary-foreground);
--color-sidebar-primary: var(--sidebar-primary);
--color-sidebar-foreground: var(--sidebar-foreground);
--color-sidebar: var(--sidebar);
--color-chart-5: var(--chart-5);
--color-chart-4: var(--chart-4);
--color-chart-3: var(--chart-3);
--color-chart-2: var(--chart-2);
--color-chart-1: var(--chart-1);
--color-ring: var(--ring);
--color-input: var(--input);
--color-border: var(--border);
--color-destructive: var(--destructive);
--color-accent-foreground: var(--accent-foreground);
--color-accent: var(--accent);
--color-muted-foreground: var(--muted-foreground);
--color-muted: var(--muted);
--color-secondary-foreground: var(--secondary-foreground);
--color-secondary: var(--secondary);
--color-primary-foreground: var(--primary-foreground);
--color-primary: var(--primary);
--color-popover-foreground: var(--popover-foreground);
--color-popover: var(--popover);
--color-card-foreground: var(--card-foreground);
--color-card: var(--card);
--radius-sm: calc(var(--radius) * 0.6);
--radius-md: calc(var(--radius) * 0.8);
--radius-lg: var(--radius);
--radius-xl: calc(var(--radius) * 1.4);
--radius-2xl: calc(var(--radius) * 1.8);
--radius-3xl: calc(var(--radius) * 2.2);
--radius-4xl: calc(var(--radius) * 2.6);
}
:root {
--background: #ffffff;
--foreground: #0f1011;
--card: #ffffff;
--card-foreground: #0f1011;
--popover: #ffffff;
--popover-foreground: #0f1011;
--primary: #5e6ad2;
--primary-foreground: #ffffff;
--secondary: #f5f6f6;
--secondary-foreground: #0f1011;
--muted: #f6f7f7;
--muted-foreground: #62666d;
--accent: #f5f6f6;
--accent-foreground: #0f1011;
--destructive: oklch(0.577 0.245 27.325);
--border: #e3e4e6;
--input: #e3e4e6;
--ring: #5e69d1;
--chart-1: #5e6ad2;
--chart-2: #828fff;
--chart-3: #7a7fad;
--chart-4: #27a644;
--chart-5: #8a8f98;
--radius: 0.625rem;
--sidebar: #f6f7f7;
--sidebar-foreground: #0f1011;
--sidebar-primary: #5e6ad2;
--sidebar-primary-foreground: #ffffff;
--sidebar-accent: #f5f6f6;
--sidebar-accent-foreground: #0f1011;
--sidebar-border: #e3e4e6;
--sidebar-ring: #5e69d1;
}
.dark {
color-scheme: dark;
--background: #010102;
--foreground: #f7f8f8;
--card: #0f1011;
--card-foreground: #f7f8f8;
--popover: #141516;
--popover-foreground: #f7f8f8;
--primary: #5e6ad2;
--primary-foreground: #ffffff;
--secondary: #18191a;
--secondary-foreground: #f7f8f8;
--muted: #141516;
--muted-foreground: #8a8f98;
--accent: #191a1b;
--accent-foreground: #f7f8f8;
--destructive: oklch(0.704 0.191 22.216);
--border: #23252a;
--input: #34343a;
--ring: #5e69d1;
--chart-1: #5e6ad2;
--chart-2: #828fff;
--chart-3: #7a7fad;
--chart-4: #27a644;
--chart-5: #d0d6e0;
--sidebar: #0f1011;
--sidebar-foreground: #f7f8f8;
--sidebar-primary: #5e6ad2;
--sidebar-primary-foreground: #ffffff;
--sidebar-accent: #18191a;
--sidebar-accent-foreground: #f7f8f8;
--sidebar-border: #23252a;
--sidebar-ring: #5e69d1;
}
@layer base {
* {
@apply border-border outline-ring/50;
}
body {
@apply bg-background text-foreground;
}
html {
@apply font-sans;
}
h1,
h2,
h3 {
@apply font-heading;
}
}5. Configuration
Utilities
Components merge class names with a cn helper. Create it (or replace it, if your project already has one):
Showing Next.js · change framework
export { cn } from "cn"components.json
This file tells the installer where to put components and where to find med-ui. Create it in your project root:
Showing Next.js · change framework
{
"style": "base-nova",
"rsc": true,
"tsx": true,
"tailwind": {
"config": "",
"css": "src/app/globals.css",
"baseColor": "neutral",
"cssVariables": true,
"prefix": ""
},
"iconLibrary": "lucide",
"aliases": {
"components": "@/components",
"utils": "@/lib/utils",
"ui": "@/components/ui",
"lib": "@/lib",
"hooks": "@/hooks"
},
"registries": {
"@med-ui": "https://ui.elhcn.com/r/{name}.json"
}
}6. Dark Mode
Dark colors apply whenever the dark class is on the <html> element.
Showing Next.js · change framework
npm install next-themesCreate a theme provider:
"use client"
import { ThemeProvider as NextThemesProvider } from "next-themes"
export function ThemeProvider(props: React.ComponentProps<typeof NextThemesProvider>) {
return <NextThemesProvider {...props} />
}Wrap your app with it in the root layout:
<html lang="en" suppressHydrationWarning>
<body>
<ThemeProvider attribute="class" defaultTheme="dark" disableTransitionOnChange>
{children}
</ThemeProvider>
</body>
</html>Call setTheme("light") or setTheme("dark") from useTheme() to build a toggle.
7. Add Components
You're ready. Install all 62 components with one command:
pnpm dlx shadcn@4.21.1 add @med-ui/allOr add only the components you need, by name:
pnpm dlx shadcn@4.21.1 add @med-ui/buttonOr install straight from a component's URL:
pnpm dlx shadcn@4.21.1 add https://ui.elhcn.com/r/button.jsonHow the Registry Works
Components live in src/components/ui. One command reads every file's imports to find its npm dependencies and the other med-ui components it needs, writes registry.json, and builds one JSON file per component into public/r:
npm run registry:build- Full index:
https://ui.elhcn.com/r/registry.json - One component:
https://ui.elhcn.com/r/button.json
Add Your Own Components
Create the component in src/components/ui:
// src/components/ui/price-tag.tsx
import { cn } from "@/lib/utils"
export function PriceTag({ className, ...props }: React.ComponentProps<"span">) {
return (
<span
className={cn("rounded-md bg-primary/10 px-2 py-0.5 text-sm text-primary", className)}
{...props}
/>
)
}Add a demo in src/components/site/demos, register it in demos/index.ts, and run npm run registry:build. The component gets its own docs page, a place in the sidebar and an install URL automatically.
Next Steps
Make it yours with Theming, connect your AI assistant with the MCP server, or browse the components.