Skip to content
Deckard
Esc
↑↓navigate↵open⌘Jpreview
On this page

Deploying

Build the deck, put it on Vercel or any Node host, set the public site URL, share a link to one slide with its own preview card, and decide who can reach the presenter window.

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

npm run build

That is next build, the script 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:

deckard validate
deckard check-overflow

A duplicate slug fails the build with a message naming both slides. 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 instead, which needs two changes to the app.

The public site URL

The sitemap and the share cards publish absolute URLs, and they need to know the deck’s address:

NEXT_PUBLIC_SITE_URL=https://talk.example.com

On Vercel you can leave it unset: a production build uses the project’s production domain (VERCEL_PROJECT_PRODUCTION_URL), and a preview build uses its branch address (VERCEL_BRANCH_URL). Set the variable when the deck lives on a custom domain.

With none of these set, both fall back to http://localhost:3000: the sitemap lists localhost URLs and every slide’s og:image points at localhost, so a shared link shows no preview. The sitemap, the root layout’s metadataBase, and the slide route’s metadataBase all read the address through deckSiteUrl() from @thebuilder/deckard-core.

Pass siteUrl to the sitemap adapter if you would rather keep the address in the file. That sets the sitemap only, so set the variable as well for the cards:

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>.

{ 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 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.

Share cards

A deck deckard init generates has app/slides/[id]/opengraph-image.tsx, so every slide URL unfurls with its own card: the slide title in the theme’s display face and colors, the brand, the slide’s position, and the deck description. next build renders them to PNGs beside the slides, one per slide, at /slides/<id>/opengraph-image. Nothing renders on request. Themes covers what the card paints with.

Two things the build needs:

  • The site URL. Vercel supplies it. Anywhere else, set NEXT_PUBLIC_SITE_URL as above before building, or the cards link to localhost.
  • Network access to Google Fonts. The build fetches the theme’s display face from fonts.googleapis.com, subset to the characters on each card. When it cannot, because the build is offline or the family is not on Google Fonts, it logs one warning per face and draws the cards in the renderer’s default face. The build still succeeds.

A deck that wants no cards deletes the route file. deckard doctor does not require it.

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.

The slides reach no font host. A built-in theme set in a real typeface ships it inside @thebuilder/deckard-themes, subset and self-hosted, and the rest use system stacks. The one font request is the share card build fetching its face from Google Fonts, at build time, not in a reader’s browser. 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 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.
/slides/<id>/opengraph-image yes One static PNG per slide, the slide’s og:image.
/sitemap.xml yes Absolute URLs from deckSiteUrl().

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 writes out/slides.pdf, one page per slide, and needs no hosting at all. Read Exporting for the flags, and Accessibility for what a PDF loses compared to the deployed deck.

Was this page helpful?