---
title: Deploying
description: Build the deck, put it on Vercel or any Node host, set the public site URL, share a link to one slide, and decide who can reach the presenter window.
sidebar:
  icon: cloud-upload
---

A deck is a Next.js app, so deploying it is deploying a Next.js app. This page covers the parts that are about the deck rather than about Next.

## Build it

```bash
npm run build
```

That is `next build`, the script [`deckard init`](/reference/cli#deckard-init) wrote. Every slide prerenders to static HTML, because the slide route reads nothing from the request.

Check the deck before you build, not after:

```bash
deckard validate
deckard check-overflow
```

A duplicate slug fails the build with a message naming both slides. [Troubleshooting](/guides/troubleshooting) has that one and the rest.

## Vercel

Import the repository and deploy. A deck generated by `deckard init` sits at the repository root with its own `package.json`, so Vercel detects Next.js and needs no build settings.

If the deck lives inside a monorepo, set the project's root directory to the deck's folder and let Vercel infer the rest.

## Any Node host

`next build` then `next start`, behind whatever process manager the host uses. The default port is 3000 and `next start -p <n>` moves it.

The deck needs a Node runtime for `next start` even though every slide is static. If the host only serves files, use [static export](/reference/requirements#static-export) instead, which needs two changes to the app.

## The public site URL

`app/sitemap.ts` builds absolute URLs, and it needs to know the deck's address:

```bash
NEXT_PUBLIC_SITE_URL=https://talk.example.com
```

Without it the sitemap falls back to `http://localhost:3000` and publishes localhost URLs. Pass `siteUrl` to the adapter instead if you would rather keep it in the file:

```ts app/sitemap.ts
export default createDeckSitemap(deck, { siteUrl: "https://talk.example.com" })
```

The variable is `NEXT_PUBLIC_`, so Next inlines it at build time. Setting it after a deploy changes nothing until the next build.

That sitemap is also how `deckard screenshots`, `check-overflow`, and `export pdf` find the slide list, so keep the route even if you do not care about search engines.

## A custom domain

Point the domain at the deployment, then set `NEXT_PUBLIC_SITE_URL` to the final address and rebuild. Nothing else in the deck holds a hostname.

## Sharing one slide

Give the slide a `slug` and link to `/slides/<slug>`.

```tsx deck/slides.tsx
{ slug: "pricing", title: "Pricing", body: <PricingSlide /> }
```

A slide with no slug is served at its 1-based position, so it is `/slides/4` until someone inserts a slide above it and it becomes `/slides/5`. A slug is the link that survives reordering. [Writing slides](/guides/writing-slides#slide-ids) covers the rules; slugs take lowercase letters, digits, and hyphens.

`/` redirects to the first slide, so the bare domain is a fine thing to hand out.

## Speaker notes are public

`notes` never render in the deck, and they do ship to the browser with the slide. They are in the prerendered HTML, so anyone who can open a slide can read them in the page source.

Write notes on the assumption that the audience can read them, or keep the deployment private until after the talk.

## The presenter window

`/presenter` is an ordinary static route and deploys with the deck. Anyone who has the URL can open it.

On its own it shows nothing. The two windows sync over a `BroadcastChannel`, which is same-origin and same-browser, so a stranger loading `/presenter` gets an empty console reading `Waiting for connection from the slideshow tab.` It never reaches your machine and never pulls your notes across the network.

The exposure that matters is the one above: the notes are in the slide page, not in the presenter route. Blocking `/presenter` does not hide them.

If you want the whole deck private, put the deployment behind the host's access control. On Vercel that is deployment protection on the project.

## Fonts and assets

Files in `public/` are served from the site root, so `public/logo.svg` is `/logo.svg`. Reference them with a root-relative path; the deck holds no base path.

A built-in theme reaches no font host. The ones set in a real typeface ship it inside `@thebuilder/deckard-themes`, subset and self-hosted, and the rest use system stacks. Adding a web font of your own is `next/font` in `app/layout.tsx`, the same as any Next app.

Images in the media blocks go through `next/image`, which needs the optimizer the host runs. That is automatic on Vercel and on any Node host running `next start`. It is the one thing [static export](/reference/requirements#static-export) has to turn off.

## What deploys and what does not

| Route | Deployed | Notes |
| --- | --- | --- |
| `/` | yes | Redirects to the first slide. |
| `/slides/<id>` | yes | One static page per slide, notes included. |
| `/presenter` | yes | Empty until a deck window on the same origin syncs to it. |
| `/sitemap.xml` | yes | Absolute URLs from `NEXT_PUBLIC_SITE_URL`. |

The deck controls, the command center, and the presenter popout are client-side and need no server of their own.

## Handing out a PDF instead

[`deckard export pdf`](/reference/cli#deckard-export-pdf) writes `out/slides.pdf`, one page per slide, and needs no hosting at all. Read [Exporting](/guides/exporting) for the flags, and [Accessibility](/guides/accessibility#html-or-pdf) for what a PDF loses compared to the deployed deck.
