Package Exports
- project-portfolio
Readme
project-portfolio
A suite of self-contained project portfolio components for Next.js App Router. Drop in a clientSlug and apiBase — each component fetches, caches, and renders everything on the server with zero client-side waterfall requests.
Requirements
- Next.js 13+ (App Router)
- React 18+
No other dependencies required.
Installation
npm install project-portfolioComponents
ProjectPortfolio
A full project grid page. Fetches all projects for a client and renders them as responsive cards (1 column on mobile, 2 on tablet, 3 on desktop).
// app/projects/page.tsx
import { ProjectPortfolio } from "project-portfolio"
// Must be a Server Component — do NOT add "use client"
export default async function ProjectsPage() {
return (
<ProjectPortfolio
clientSlug="your-client-slug"
apiBase="https://your-api.com"
basePath="/projects"
/>
)
}| Prop | Type | Required | Default | Description |
|---|---|---|---|---|
clientSlug |
string |
Yes | — | Identifies which client's projects to load |
apiBase |
string |
Yes | — | Base URL of the projects API |
basePath |
string |
No | "/projects" |
Base path for project detail links |
revalidate |
number |
No | 60 |
Cache revalidation period in seconds |
ProjectDetail
A full project detail page. Fetches a single project by slug and renders its gallery, custom fields, related projects, and a back link.
// app/projects/[slug]/page.tsx
import { ProjectDetail } from "project-portfolio"
// Must be a Server Component — do NOT add "use client"
export default async function ProjectPage({ params }: { params: { slug: string } }) {
return (
<ProjectDetail
slug={params.slug}
clientSlug="your-client-slug"
apiBase="https://your-api.com"
backPath="/projects"
backLabel="All Projects"
/>
)
}| Prop | Type | Required | Default | Description |
|---|---|---|---|---|
slug |
string |
Yes | — | The project slug to load |
clientSlug |
string |
Yes | — | The client slug that owns this project |
apiBase |
string |
Yes | — | Base URL of the projects API |
backPath |
string |
No | "/projects" |
Path for the back navigation link |
backLabel |
string |
No | "All Projects" |
Label for the back navigation link |
revalidate |
number |
No | 60 |
Cache revalidation period in seconds |
ProjectMenu
A compact megamenu component. Shows featured projects as list-style cards on the left and "Browse By" filter links on the right. Designed to be dropped directly into a navigation megamenu dropdown.
// components/navigation/MegaMenu.tsx
import { ProjectMenu } from "project-portfolio"
// Must be a Server Component — do NOT add "use client"
export async function ProjectsMegaMenu() {
return (
<ProjectMenu
clientSlug="your-client-slug"
apiBase="https://your-api.com"
basePath="/projects"
subtitle="Our systems are installed in every geographic region of the U.S. and in a variety of applications."
maxProjects={6}
/>
)
}| Prop | Type | Required | Default | Description |
|---|---|---|---|---|
clientSlug |
string |
Yes | — | Identifies which client's projects to load |
apiBase |
string |
Yes | — | Base URL of the projects API |
basePath |
string |
No | "/projects" |
Base path for project detail links |
viewAllPath |
string |
No | Same as basePath |
Path for the "View All Projects" link |
subtitle |
string |
No | — | Description paragraph shown above the project cards |
font |
string |
No | System font stack | Font family string applied to all inline styles |
maxProjects |
number |
No | 6 |
Maximum number of projects to display |
revalidate |
number |
No | 60 |
Cache revalidation period in seconds |
The filter options in the right sidebar are driven automatically by the is_filterable fields returned from the API schema — no manual configuration needed.
Important: Server Component Usage
All three components (ProjectPortfolio, ProjectDetail, ProjectMenu) are async Server Components. They must be rendered in a server context:
// CORRECT
export default async function Page() {
return <ProjectPortfolio clientSlug="..." apiBase="..." />
}
// WRONG — causes infinite client-side fetch loop
"use client"
export default function Page() {
return <ProjectPortfolio clientSlug="..." apiBase="..." />
}If a parent component uses "use client", these components cannot be rendered inside it directly. Pass them as children from a server component instead.
Caching
Data is cached at two levels:
- Per-render —
React.cache()deduplicates any duplicate calls within the same render pass - Cross-request —
next: { revalidate: N }caches responses in Next.js's built-in Data Cache
No third-party caching libraries are required.
Publishing to npm
# Log in to npm
npm login
# Build and publish
cd package
npm run build
npm publish --access publicTo release an update, bump the version field in package/package.json then run npm publish again.