# Quickstart

The setup CLI adds a working portfolio to a Next.js App Router site in one
command: the routes, a starter renderer you own, the webhook Sonor calls when
content changes, and the route behind the dashboard's Preview button. This
page covers its flags, the files it writes and the three things to finish by
hand.

## Before you start

You need a Next.js App Router project, the two packages, and your project's
API key:

```bash
pnpm add @sonordev/agency-site-kit @sonordev/site-kit
```

```bash
# .env.local
SONOR_API_KEY=sonor_xxxxxxxx_xxxxxxxxxxxx
```

## Run the CLI

From your project root:

```bash
npx agency-site-kit-setup --site-url https://youragency.com --agency-name "Your Agency"
```

It looks for `app/` first, then `src/app/`, and writes everything under the
one it finds. If there's neither, it stops with an error and writes nothing.

| Flag            | Default                 | What it sets                                                                                                                                           |
| --------------- | ----------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------ |
| `--base-path`   | `/work`                 | Where case studies live. `--base-path /projects` puts the index at `/projects` and each case study at `/projects/<slug>`. A trailing slash is dropped. |
| `--site-url`    | `https://YOUR-SITE.com` | Your site's origin, written into the index's canonical URLs and each case study's JSON-LD. A trailing slash is dropped.                                |
| `--agency-name` | `Your Agency`           | The publisher named in each case study's JSON-LD.                                                                                                      |

Give each flag its value as the next argument (`--base-path /projects`,
not `--base-path=/projects`). If you leave out `--site-url`, search the
generated files for `https://YOUR-SITE.com` and replace it.

## What it writes

With the default base path:

| File                                    | What it is                                                                                                                    |
| --------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------- |
| `app/work/page.tsx`                     | The index, with a stable title and canonical URL per page                                                                     |
| `app/work/category/[category]/page.tsx` | One page per category. They're real links, so crawlers find every case study                                                  |
| `app/work/[slug]/page.tsx`              | Each case study, statically generated, with Article JSON-LD                                                                   |
| `app/work/error.tsx`                    | What a visitor sees if Sonor can't be reached on a page's first render                                                        |
| `app/work/_components/CaseStudy.tsx`    | Your case study renderer: hero, challenges, strategy, results, testimonial, gallery and call to action                        |
| `app/work/_components/WorkGrid.tsx`     | Your index: category links, cards and pagination                                                                              |
| `app/work/_components/work.module.css`  | Starter styles, built on the `--sk-*` brand variables                                                                         |
| `app/api/seo-revalidate/route.ts`       | The webhook Sonor calls when content changes (see [Live updates and preview](https://sonor.dev/agency-site-kit/live-updates)) |
| `app/api/portfolio-preview/route.ts`    | The dashboard's Preview button (same page)                                                                                    |

The CLI never overwrites a file. Anything that already exists is reported as
`skipped (exists)` and left alone, so running it again only fills in what's
missing. The leading underscore on `_components` keeps that folder out of
routing.

## Finish up

### 1. Wrap your layout

`AgencySiteKitLayout` fetches your brand from Sonor and publishes it as CSS
variables the starter styles read. It also gives site-kit's client modules
(analytics, forms, chat) the short-lived token they need.

```tsx
// app/layout.tsx
import { AgencySiteKitLayout } from '@sonordev/agency-site-kit';

export default function RootLayout({ children }: { children: React.ReactNode }) {
  return (
    <html lang="en">
      <body>
        <AgencySiteKitLayout>{children}</AgencySiteKitLayout>
      </body>
    </html>
  );
}
```

Already using site-kit's own `SiteKitLayout`? See [Brand layout](https://sonor.dev/agency-site-kit/layout)
for how the two fit together.

### 2. Add case studies to your sitemap

```ts
// app/sitemap.ts
import type { MetadataRoute } from 'next';
import { generatePortfolioSitemapEntries } from '@sonordev/agency-site-kit/portfolio/server';

export default async function sitemap(): Promise<MetadataRoute.Sitemap> {
  const work = await generatePortfolioSitemapEntries({ baseUrl: 'https://youragency.com' });
  return [
    { url: 'https://youragency.com', changeFrequency: 'weekly', priority: 1 },
    ...work,
  ];
}
```

Pass the same `basePath` you gave the CLI if it isn't `/work`.

### 3. Make it yours

`CaseStudy.tsx` and `WorkGrid.tsx` are plain server components with no
client JavaScript, and nothing in the kit depends on their class names or
markup. Restyle them, reorder them, or replace them. The kit keeps supplying
the data and the rules, whatever the page looks like.

The starter renders the seven sections most case studies carry. Every other
section's data is typed too (see [Types](https://sonor.dev/agency-site-kit/types)), so adding one is a new
`case` in `CaseStudy.tsx`'s switch. For example, a Lighthouse block for the
Performance section:

```tsx
case 'portfolioPerformance': {
  const data = section.data as Data<'portfolioPerformance'>;
  if (!data.after) return null;
  return (
    <section className={styles.section}>
      <h2>Performance</h2>
      <p>
        Performance {data.after.performance ?? '–'}, SEO {data.after.seo ?? '–'}, accessibility{' '}
        {data.after.accessibility ?? '–'}
      </p>
    </section>
  );
}
```

If you add motion, keep it below the fold. The hero is the page's largest
paint and has to be visible in the server-rendered HTML. `@sonordev/site-kit`
has [motion components](https://sonor.dev/site-kit/motion) built for that.

## How the starter behaves

- **Every page is a server component.** The one client component is
  `error.tsx`, because Next requires error boundaries to be.
- **The index shows 12 case studies a page**, with `?page=2` and so on for
  the rest. Category links appear once there's more than one category. The
  first two card images load eagerly and the rest lazily.
- **A category route carries a slug** (`web-development`). `WorkGrid` looks
  the slug up in `getPortfolioCategories()` to get the stored name the API
  filters on.
- **Numbers carry their credit.** Hero figures and result cards run through
  `curatePortfolioProof` and print `metricNote` under anything you didn't
  measure, and each card leads with `headlineKpi`. See
  [Proof and provenance](https://sonor.dev/agency-site-kit/proof).
- **A missing case study is a 404, an outage isn't.** `getPortfolioItem`
  returns `null` for a slug that doesn't exist and throws when Sonor can't be
  reached, so an already-built page keeps serving its last good copy. See
  [Fetching](https://sonor.dev/agency-site-kit/fetching).

## Check it

Build, start the server, then fetch the index and look for real content in
the HTML:

```bash
pnpm build && pnpm start
curl -s http://localhost:3000/work | grep -c '<h1'
```

A count of zero means the page didn't render on the server. Once the site is
deployed, open a draft case study in the Sonor dashboard and press Preview: it
should open on your site with the draft showing.
