Nexus CRM
A production-ready CRM dashboard template built with Next.js 16, TypeScript, Tailwind CSS v4, and shadcn/ui. Dark mode by default, fully responsive, and driven by realistic mock data so you can connect your own backend in minutes.
Template dashboard CRM siap produksi, dibikin pakai Next.js 16, TypeScript, Tailwind CSS v4, dan shadcn/ui. Dark mode by default, responsif penuh, dan datanya pakai mock data yang realistis — jadi kamu bisa sambungin ke backend sendiri cuma dalam hitungan menit.
Thank you for purchasing Nexus CRM! This document covers everything you need to install, customize, and ship the template. If you get stuck, jump to Support.
Makasih udah beli Nexus CRM! Dokumen ini ngebahas semua yang kamu butuhin buat install, custom, sampai rilis template-nya. Kalau ada yang nyangkut, langsung lompat ke bagian Bantuan.
FeaturesFitur
any.Strict mode, nol any.RequirementsKebutuhan
- Node.js ≥ 20 (LTS recommended)
- pnpm ≥ 9 — install with
npm install -g pnpm - A modern browser (Chrome, Firefox, Safari, Edge)
- Node.js ≥ 20 (mending versi LTS)
- pnpm ≥ 9 — install pakai
npm install -g pnpm - Browser modern (Chrome, Firefox, Safari, Edge)
Using npm / yarn / bun? The project ships with pnpm, but any package manager works — delete pnpm-lock.yaml and install with your tool of choice.
Pakai npm / yarn / bun? Default-nya emang pnpm, tapi package manager apa aja bisa kok — hapus pnpm-lock.yaml-nya, terus install pakai tool favoritmu.
Quick StartMulai Cepat
# 1. Install dependencies
pnpm install
# 2. Start the dev server
pnpm dev
# → http://localhost:3000 (redirects to /dashboard)
# 3. Production build
pnpm build
pnpm start
# 1. Install dependency
pnpm install
# 2. Jalanin dev server
pnpm dev
# → http://localhost:3000 (otomatis diarahin ke /dashboard)
# 3. Build produksi
pnpm build
pnpm start
Available scriptsDaftar script
| ScriptScript | DescriptionFungsinya |
|---|---|
pnpm dev | Start the dev server (Turbopack)Jalanin dev server (Turbopack) |
pnpm build | Production buildBuild buat produksi |
pnpm start | Serve the production buildJalanin hasil build produksi |
pnpm lint | Run ESLintJalanin ESLint |
No configuration needed to run. The template ships with mock data and requires no environment variables. Install, run, done.
Nggak perlu setting apa-apa buat jalan. Template-nya udah pakai mock data dan nggak butuh environment variable sama sekali. Install, jalanin, kelar.
Tech Stack
| LayerBagian | TechnologyTeknologi |
|---|---|
| Framework | Next.js 16 (App Router) |
| LanguageBahasa | TypeScript 5 (strict) |
| Styling | Tailwind CSS v4 + tw-animate-css |
| UI primitivesKomponen UI | shadcn/ui (Radix UI) |
| ChartsChart | Recharts 3 |
| AnimationAnimasi | Framer Motion 12 |
| IconsIkon | Lucide React |
| ThemingTema | next-themes |
| Toast | Sonner |
| Package manager | pnpm |
Project StructureStruktur Project
src/
├── app/
│ ├── (auth)/ # Auth layout group
│ │ ├── login/
│ │ └── register/
│ ├── (dashboard)/ # App layout group (sidebar + header)
│ │ ├── layout.tsx # Sidebar + Header wrapper
│ │ ├── dashboard/
│ │ ├── leads/ + [id]/ # Kanban + lead detail
│ │ ├── deals/ + [id]/
│ │ ├── contacts/ + [id]/
│ │ ├── organizations/ + [id]/
│ │ ├── notes/
│ │ ├── tasks/
│ │ ├── notifications/
│ │ └── settings/
│ ├── globals.css # Design tokens + Tailwind theme
│ ├── layout.tsx # Root layout (fonts, ThemeProvider, Toaster)
│ └── page.tsx # Redirect → /dashboard
│
├── components/
│ ├── ui/ # shadcn/ui primitives
│ ├── shared/ # Reusable app components (PageHeader, StatusBadge…)
│ ├── layout/ # Sidebar, Header
│ ├── charts/ # Recharts wrappers
│ └── [feature]/ # Page-specific components (contacts/, tasks/…)
│
├── lib/
│ ├── mock-data.ts # ALL mock data + accessor helpers
│ ├── format.ts # formatCurrency, formatDate, getInitials…
│ ├── nav.ts # Sidebar navigation config
│ └── utils.ts # cn() classname helper
│
└── types/
└── index.ts # ALL TypeScript interfaces
The two files you'll touch most: src/lib/mock-data.ts (data) and src/types/index.ts (shapes).
Dua file yang paling sering kamu utak-atik: src/lib/mock-data.ts (datanya) dan src/types/index.ts (tipe datanya).
CustomizationKustomisasi
Colors & themeWarna & tema
All colors are CSS custom properties in src/app/globals.css. Edit the :root block (light mode) and the .dark block (dark mode) — nothing else references raw hex values.
Semua warna disimpan sebagai CSS custom property di src/app/globals.css. Edit blok :root (light mode) dan blok .dark (dark mode) — nggak ada tempat lain yang nyimpen nilai hex langsung.
:root {
--primary: #3b82f6; /* accent (blue-500) */
--primary-hover: #2563eb;
--background: #ffffff;
--foreground: #111827;
/* … */
}
.dark {
--background: #0a0a0a;
--foreground: #f9fafb;
/* … */
}
Change --primary to rebrand the entire app in one line.
Ganti --primary aja, dan brand seluruh aplikasi langsung berubah cuma dari satu baris.
FontsFont
Fonts are loaded in src/app/layout.tsx via next/font/google (Geist Sans + Geist Mono). Swap them for any Google or local font there.
Font di-load di src/app/layout.tsx lewat next/font/google (Geist Sans + Geist Mono). Mau ganti ke font Google lain atau font lokal? Ubah di situ.
Sidebar navigationMenu sidebar
Edit src/lib/nav.ts — add, remove, or reorder items. Each item is { label, href, icon } using a Lucide icon. Header page titles are derived from this file automatically.
Edit src/lib/nav.ts — mau nambah, hapus, atau ubah urutan menu tinggal di sini. Tiap item bentuknya { label, href, icon } pakai ikon Lucide. Judul halaman di header otomatis ngikut file ini.
export const mainNav: NavItem[] = [
{ label: "Dashboard", href: "/dashboard", icon: LayoutDashboard },
{ label: "Leads", href: "/leads", icon: UserPlus },
// …
];
Adding a new pageNambah halaman baru
- Create
src/app/(dashboard)/your-page/page.tsx. - Add an entry to
mainNavinsrc/lib/nav.ts. - Build page-specific components under
src/components/your-page/.
- Bikin
src/app/(dashboard)/your-page/page.tsx. - Tambahin entri ke
mainNavdisrc/lib/nav.ts. - Taruh komponen khusus halaman itu di
src/components/your-page/.
Avatar
Avatars are generated locally as inline SVG data URIs (gradient + initials) by the avatar() function in src/lib/mock-data.ts. No network requests — they render offline. To use real photos, drop images in /public/avatars/ and return their paths from avatar().
Avatar dibikin lokal sebagai SVG data URI inline (gradient + inisial) lewat fungsi avatar() di src/lib/mock-data.ts. Nggak ada request jaringan — tetap muncul walau offline. Kalau mau pakai foto asli, taruh gambarnya di /public/avatars/, terus return path-nya dari avatar().
Mock Data → Real APIMock Data → API Asli
Everything renders from src/lib/mock-data.ts — there are no live API calls anywhere. All shapes are defined in src/types/index.ts, so your API just needs to return matching JSON. Components import mock data directly today. The migration is the same everywhere: fetch on the server, pass data down as props.
Semua tampilan dirender dari src/lib/mock-data.ts — nggak ada panggilan API beneran sama sekali. Semua tipe data udah didefinisiin di src/types/index.ts, jadi API kamu tinggal balikin JSON yang bentuknya sama. Sekarang komponen masih import mock data langsung. Pola pindahnya sama semua: fetch di server, terus lempar datanya sebagai props.
Pattern A — Server Components (list & detail pages)Pola A — Server Component (halaman list & detail)
Pages like contacts/page.tsx are async-capable Server Components. Lift the data import up to the page as a fetch, then pass it into the presentational component as a prop.
Halaman kayak contacts/page.tsx itu Server Component yang bisa async. Pindahin import datanya ke halaman jadi fetch, terus lempar ke komponen tampilannya sebagai prop.
// src/app/(dashboard)/contacts/page.tsx
import type { Contact } from "@/types";
import { ContactsTable } from "@/components/contacts/contacts-table";
async function getContacts(): Promise<Contact[]> {
const res = await fetch(`${process.env.API_URL}/contacts`, {
next: { revalidate: 60 }, // ISR — or cache: "no-store"
});
if (!res.ok) throw new Error("Failed to load contacts");
return res.json();
}
const ContactsPage = async () => {
const contacts = await getContacts();
return <ContactsTable contacts={contacts} />;
};
Recommended: a thin data layer. Create src/lib/api/ functions that mirror the helper names already in mock-data.ts (getContact, getOrganizationDeals, …). Then swapping mock → real is a one-line import change per page.
Saran: bikin data layer tipis. Bikin fungsi di src/lib/api/ yang namanya niru helper yang udah ada di mock-data.ts (getContact, getOrganizationDeals, …). Jadi ganti dari mock ke API asli cukup ubah satu baris import per halaman.
Pattern B — Client Components (Notes, Tasks, filterable tables)Pola B — Client Component (Notes, Tasks, tabel berfilter)
Components marked "use client" (e.g. tasks/task-list.tsx) seed React state from mock data:
Komponen yang ada "use client"-nya (misal tasks/task-list.tsx) ngisi state React dari mock data:
const [tasks, setTasks] = useState<Task[]>(initialTasks);
For these, either pass the initial data as props from an async parent Server Component (preferred), or fetch on the client with SWR / React Query and persist mutations through Server Actions. The CRUD handlers already exist (save, remove, toggle) — replace the setState calls with your API mutation plus an optimistic update.
Buat kasus ini, lempar data awalnya sebagai props dari Server Component induk yang async (paling recommended), atau fetch di client pakai SWR / React Query dan simpan perubahannya lewat Server Actions. Handler CRUD-nya udah ada (save, remove, toggle) — tinggal ganti setState-nya jadi mutasi API kamu plus optimistic update.
The data contractKontrak data
| EntityEntitas | TypeTipe | Mock exportExport mock |
|---|---|---|
| Contacts | Contact | contacts, getContact |
| Leads | Lead | leads, getLead |
| Deals | Deal | deals, getDeal |
| Organizations | Organization | organizations, getOrganization |
| Notes | Note | notes |
| Tasks | Task | tasks |
| Users / teamUser / tim | User | currentUser, teamMembers |
| Dashboard metricsMetrik dashboard | Metric | dashboardMetrics |
Environment variables
Copy .env.example to .env.local and fill in your values:
Copy .env.example jadi .env.local, terus isi nilainya:
API_URL=https://api.yourbackend.com
Deployment
Standard Next.js app — deploys anywhere Node runs.
Aplikasi Next.js biasa — bisa di-deploy di mana aja yang bisa jalanin Node.
- Vercel (recommended): import the repo, it auto-detects Next.js. Zero config.
- Netlify / Cloudflare / Render: use the Next.js adapter/preset.
- Self-host / Docker:
pnpm buildthenpnpm startbehind a reverse proxy.
- Vercel (paling gampang): tinggal import repo-nya, Next.js kedeteksi otomatis. Zero config.
- Netlify / Cloudflare / Render: pakai adapter/preset Next.js.
- Self-host / Docker:
pnpm buildteruspnpm startdi belakang reverse proxy.
Set any environment variables in your host's dashboard.
Set environment variable-nya di dashboard hosting kamu.
Credits & Third-Party LicensesKredit & Lisensi Pihak Ketiga
All third-party packages are open source and permissively licensed. No attribution is required in your end product; they are listed here for transparency.
Semua paket pihak ketiga open source dan lisensinya permisif. Nggak wajib nyantumin atribusi di produk akhirmu; daftar ini cuma buat transparansi.
| Asset / LibraryAset / Library | LicenseLisensi |
|---|---|
| Next.js, React, React DOM | MIT |
| Radix UI / shadcn/ui | MIT |
| Recharts | MIT |
| Framer Motion | MIT |
| Sonner, next-themes, clsx, tailwind-merge | MIT |
| Tailwind CSS, tw-animate-css | MIT |
| class-variance-authority | Apache-2.0 |
| Lucide icons | ISC |
| Geist Sans / Geist Mono (fonts) | SIL Open Font License 1.1 |
Avatars are 100% self-contained. Generated locally as inline SVG data URIs — no external services, no network requests, no attribution needed.
Avatar 100% mandiri. Dibikin lokal sebagai SVG data URI inline — tanpa layanan eksternal, tanpa request jaringan, dan nggak perlu atribusi.
Changelog
v1.0.0 Initial releaseRilis pertama
- First public release.
- Rilis publik pertama.
SupportBantuan
For questions, bug reports, or customization help, contact:
Buat pertanyaan, laporan bug, atau bantuan kustomisasi, hubungi:
your-support-email@example.com
Please include your purchase code and a clear description (screenshots help). We aim to respond within 1–2 business days.
Sertakan kode pembelian dan jelasin masalahnya sejelas mungkin (screenshot sangat membantu). Kami usahain bales dalam 1–2 hari kerja.