@vserve_digital/ui
v0.2.5
Published
Server-first (React Server Components), zero-config UI sections for Next.js 15/16 App Router.
Keywords
Readme
@vserve_digital/ui
Server-first (React Server Components), zero-config UI sections for Next.js 15/16 App Router.
Features
- 100% Server-First (RSC Donut Architecture):
<Services />ships 0 KB of client JS;<Navbar />renders navigation columns and featured cards on the server and isolates client interactivity strictly to the menu toggle/overlay shell. - Zero-Config CSS Modules: Scoped styles are automatically imported by each section—no
transpilePackagesand no manual CSS imports required. - Flexible Card Counts:
<Services />: Every item renders as a 100% full-width horizontal row—pass1,2,3, or6+items and the layout stacks cleanly with zero empty grid gaps.<Navbar />: Pass1–3navigation columns and1–2featured project cards (1card auto-fits to 100% width;2cards render 50%/50%).
- Built-in XSS & Runtime Safety: Sanitizes
javascript:,data:, and protocol-relative//URLs, preventsnext/imageinvalid scheme crashes, and provides runtime type guards (isNavbarProps,isServicesProps) for untyped CMS/API JSON.
Installation
pnpm add @vserve_digital/ui
# or
npm install @vserve_digital/uiQuick Start (Copy-Paste)
1. Define Type-Safe Data (src/app/data.ts)
import type {
NavbarBrand,
NavbarColumn,
NavbarFeaturedProject,
ServicesProps,
} from '@vserve_digital/ui';
export const brand = {
name: 'Nakshatra',
href: '/',
// logoUrl: '/logo.svg', // Optional custom logo image
} as const satisfies NavbarBrand;
export const columns = [
{
title: 'Platform',
links: [
{ label: 'Home', href: '/' },
{ label: 'About', href: '/about' },
{ label: 'Pricing', href: '/pricing' },
],
},
{
title: 'Legal',
links: [
{ label: 'Privacy Policy', href: '/privacy' },
{ label: 'Docs', href: 'https://example.com', isExternal: true },
],
},
] as const satisfies readonly NavbarColumn[];
export const featuredProjects = [
{
id: 'p1',
title: 'Designing cards that people actually use',
category: 'PRODUCT',
imageUrl: 'https://images.unsplash.com/photo-1618005182384-a83a8bd57fbe?q=80&w=400&h=300&fit=crop',
href: '/projects/cards',
},
] as const satisfies readonly NavbarFeaturedProject[];
export const servicesData = {
label: 'Services',
primaryText: 'How We',
secondaryText: 'Can Help You',
items: [
{
id: 's1',
number: '01',
title: 'Design',
description: 'High-converting product design and scalable design systems.',
imageUrl: '/buy.webp',
href: '/services/design',
},
{
id: 's2',
number: '02',
title: 'Engineering',
description: 'Server-first Next.js architecture built for instant paint.',
href: '/services/engineering',
},
],
} as const satisfies ServicesProps;2. Render in src/app/page.tsx
import { Navbar } from '@vserve_digital/ui/navbar';
import { Services } from '@vserve_digital/ui/services';
import { brand, columns, featuredProjects, servicesData } from './data';
export default function Page() {
return (
<main>
<Navbar
brand={brand}
columns={columns}
featuredProjects={featuredProjects}
/>
<Services {...servicesData} />
</main>
);
}Data Guide — How Many Items Should You Pass?
| Section | Prop | Type | Recommended Count | Behavior When Passing Fewer/More Items |
| :--- | :--- | :--- | :--- | :--- |
| <Navbar /> | brand | NavbarBrand (optional) | 1 object | Defaults to { name: 'ballance', href: '/' } with built-in globe SVG if omitted. |
| <Navbar /> | columns | readonly NavbarColumn[] | 1 – 3 columns (2–6 links each) | Flex-wraps on tablet (<=1024px) and stacks vertically on mobile (<=768px). |
| <Navbar /> | featuredProjects | readonly NavbarFeaturedProject[] | 1 or 2 cards | Uses repeat(auto-fit, minmax(220px, 1fr)): 1 card stretches to 100% width; 2 cards sit 50%/50%. |
| <Services /> | items | readonly ServiceItem[] | Any count (1, 2, 3, 6+) | Each card is a 100% full-width horizontal row—2 items render 2 clean rows, 5 items render 5 rows. |
Using with Headless CMS / External JSON APIs
When fetching untyped JSON at runtime, use isNavbarProps and isServicesProps:
import { Navbar, isNavbarProps } from '@vserve_digital/ui/navbar';
import { Services, isServicesProps } from '@vserve_digital/ui/services';
export default async function CmsPage() {
const res = await fetch('https://api.example.com/sections');
const data: unknown = await res.json();
return (
<main>
{isNavbarProps(data) && <Navbar {...data} />}
{isServicesProps(data) && <Services {...data} />}
</main>
);
}Remote Images (next.config.ts)
If you pass external image URLs (e.g., images.unsplash.com or your CMS CDN) in imageUrl, allow the domain in your consumer's next.config.ts:
import type { NextConfig } from 'next';
const nextConfig: NextConfig = {
images: {
remotePatterns: [
{ protocol: 'https', hostname: 'images.unsplash.com' },
],
},
};
export default nextConfig;