Quick start
Prerequisites, registry setup, and installing your first section.
Prerequisites
MKT UI sections are plain React. They are tested against the combination below; other recent versions very likely work, but this is what is actually verified.
| Requirement | Verified version |
|---|---|
| React | 19.2 |
| TypeScript | 5.9 |
| Tailwind CSS | 4.3 |
| shadcn CLI | 4.21 |
| Node.js | 20.9+ |
You need a project that already has Tailwind CSS v4 and shadcn initialised. If you do not, run this first:
npx shadcn@latest initSections are framework-neutral: no next/link, no next/image, no routing, no server
actions. They work in Next.js, Vite, React Router, or Astro. A lint rule in this
repository enforces that, so it cannot quietly stop being true.
1. Add the registry
Add the @mkt namespace to your components.json:
{
"registries": {
"@mkt": "http://localhost:3000/r/{name}.json"
}
}MKT UI is not published yet
The URL above points at a local development server, because there is no public
registry deployment yet. Until there is, run this repository locally
(pnpm dev) to install from it, or copy the source straight out of
registry/ — every section is a single self-contained file.
2. Install a section
npx shadcn@latest add @mkt/hero-splitThat one command also installs everything the section needs: the container,
section, cta-group, and media-frame foundations, plus any shadcn primitives.
Each section declares its own dependencies, so installing one never pulls in the
whole catalog.
3. Add the theme tokens
Sections reference semantic tokens that are not part of stock shadcn. Add them to your global stylesheet:
@theme inline {
--color-brand: var(--brand);
--color-brand-foreground: var(--brand-foreground);
--color-brand-subtle: var(--brand-subtle);
--color-brand-border: var(--brand-border);
--color-surface: var(--surface);
--color-surface-foreground: var(--surface-foreground);
--color-success: var(--success);
--color-success-foreground: var(--success-foreground);
--color-success-subtle: var(--success-subtle);
}
@theme {
--container-mkt: 80rem;
--container-mkt-narrow: 56rem;
--container-mkt-prose: 44rem;
--text-display: 2.75rem;
--text-display--line-height: 1.08;
--text-display--letter-spacing: -0.027em;
--text-display-lg: 3.5rem;
--text-display-lg--line-height: 1.04;
--text-display-lg--letter-spacing: -0.032em;
--text-title: 2rem;
--text-title--line-height: 1.15;
--text-title--letter-spacing: -0.02em;
--text-lead: 1.1875rem;
--text-lead--line-height: 1.6;
--text-lead--letter-spacing: -0.006em;
}
:root {
--brand: oklch(0.514 0.222 16.935);
--brand-foreground: oklch(0.99 0 0);
--brand-subtle: oklch(0.969 0.019 17.585);
--brand-border: oklch(0.892 0.058 17.585);
--surface: oklch(0.977 0.002 265);
--surface-foreground: oklch(0.145 0 0);
--success: oklch(0.52 0.122 158);
--success-foreground: oklch(0.99 0 0);
--success-subtle: oklch(0.962 0.03 158);
}See Theming for what each token controls and how to make it yours.
4. Use it
import { Button } from "@/components/ui/button";
import { HeroSplit } from "@/components/hero-split";
export default function Page() {
return (
<HeroSplit
eyebrow="Licensed & insured"
title="Plumbing problems fixed properly, the first time."
description="Same-day emergency slots across north Austin, upfront pricing, and a six-year workmanship warranty."
actions={
<>
<Button asChild>
<a href="#quote">Request a quote</a>
</Button>
<Button variant="outline" asChild>
<a href="tel:+15125550142">Call (512) 555-0142</a>
</Button>
</>
}
note="Free quotes · No call-out fee on scheduled work"
image={{ src: "/hero.jpg", alt: "Technician fitting a copper pipe" }}
/>
);
}Where files land
The shadcn CLI places files using your components.json aliases and rewrites the
imports to match:
| Registry type | Installs to | Example |
|---|---|---|
registry:block | aliases.components | components/hero-split.tsx |
registry:component | aliases.components | components/container.tsx |
registry:hook | aliases.hooks | hooks/use-form-submission.ts |
registry:ui | aliases.ui | components/ui/button.tsx |
Server and client rendering
Most sections render on the server and ship no JavaScript. The ones that need
interaction carry their own "use client" boundary, so you can drop them into a
server component without wrapping anything yourself.
| Section | Boundary |
|---|---|
hero-split, services-icon-grid, cta-band, site-footer | Server |
site-header | Client (mobile drawer state) |
quote-request-form | Client (form state) |