Next.js
Use the Format Next.js plugin to compile and render your documents inside a Next.js app.
The Format Next.js plugin compiles your documents at build time, registers module aliases for Webpack and Turbopack, and handles caching, type generation, and deployment configuration automatically.
To give you some upfront context, this is how a Format project would typically sit within a Next.js application:
app
api
format
documents
invoice
assets
data
Installation
To add Format to an existing codebase or repo, you can use npx format init to automate the setup. This will add your Format config, the npm scripts, the gitignore entries, and the packages. Each step will confirm before editing anything.
# Install the Format CLI if you haven't already
npm install "@format.dev/cli" --save-dev
# Inside your repo directory, run the Format project init script
npx format init --root-dir ./format# Install the Format CLI if you haven't already
pnpm add "@format.dev/cli" --save-dev
# Inside your repo directory, run the Format project init script
pnpm dlx format init --root-dir ./format# Install the Format CLI if you haven't already
yarn add "@format.dev/cli" --dev
# Inside your repo directory, run the Format project init script
yarn dlx format init --root-dir ./format# Install the Format CLI if you haven't already
bun add "@format.dev/cli" --dev
# Inside your repo directory, run the Format project init script
bun x format init --root-dir ./formatRunning the init script will bootstrap your project and make it Format-ready. We use the --root-dir option to separate your Format documents and code from your existing app, but this is fully configurable if you would like to use a different folder structure.
If you'd prefer to add all the initial required files manually, you can follow the manual installation steps.
Add the compile package
Our Next.js plugin ships with the @format.dev/compile package. Add it using the Format CLI:
npx format add "@format.dev/compile"pnpm dlx format add "@format.dev/compile"yarn dlx format add "@format.dev/compile"bun x format add "@format.dev/compile"Update your Next.js config
Wrap your Next.js config with withFormat.
import type { NextConfig } from 'next'
import withFormat from '@format.dev/compile/next'
const nextConfig: NextConfig = {}
export default withFormat()(nextConfig)The withFormat plugin will:
- Compile your Format documents when your Next.js project starts up
- Cache Format builds, for efficient performance
- Make your Format document renderers available via the import statement:
import { myPdf } from '@format:documents'. Check the example usage to learn more. - Automatically rebuild on changes in development, so you can develop Format document templates and styles without restarting any servers
- Configures Next.js for deployments that include Format PDFs and their assets
Developer workflow
Learn how building PDFs fits into your development workflow.
Launch Format Studio
Open Format Studio using the npm script added earlier.
# Either:
npm run dev
# Or this, if npm run dev was taken:
npm run format:dev# Either:
pnpm run dev
# Or this, if pnpm run dev was taken:
pnpm run format:dev# Either:
yarn dev
# Or this, if yarn dev was taken:
yarn format:dev# Either:
bun run dev
# Or this, if bun run dev was taken:
bun run format:devYou will be prompted to sign in to Format or create an account. Format Studio will then be launched on: http://localhost:1234. You can now start designing and building your PDF templates in format/documents, testing out dynamic data, styles and layouts.
Launch Next.js
In a new terminal, run the Next.js dev server
npm run devpnpm run devyarn devbun run devFollow the guide below to add a Next.js route that generates PDFs from your documents. Hit that route and instantly get a PDF with your latest changes.
You can continue to iterate in Format Studio and your Next.js route will always pull in the latest template changes.
Example usage (API route)
Below is a simple example of how to use Format in Next.js.
This example creates a server-side app route that generates the invoice PDF and returns an "inline PDF" to the browser. The browser will display the PDF in the built-in PDF viewer.
import { invoice } from '@format:documents'
import { FormatClient } from '@format.dev/client'
export async function GET() {
const doc = await invoice.render({ customerName: 'Ada Lovelace' })
const format = new FormatClient()
const response = await format.pdf(doc)
return new Response(response.body, {
headers: {
'Content-Type': 'application/pdf',
'Content-Disposition': 'inline; filename="invoice.pdf"'
}
})
}See the Renderer API reference for the full method documentation.
Although this is a common way to generate a PDF, it's not the only use-case in Next.js. You can also generate PDFs in client code or in edge functions.
Multi-target builds
By default, withFormat compiles a single node target. To generate PDFs in the browser or at the edge, add one or more explicit targets.
For instance, the following config would allow you to generate PDFs in API routes on the server and in the browser.
import type { NextConfig } from 'next'
import withFormat from '@format.dev/compile/next'
const nextConfig: NextConfig = {}
export default withFormat({
targets: [
{ target: 'node' },
{ target: 'browser', assets: 'dynamic' }
]
})(nextConfig)@format:documents always resolves to the first target in the array. Every target also gets a suffixed import path:
| Target | Suffixed import | Assets | Dependencies |
|---|---|---|---|
node | @format:documents/node | Filesystem (auto) | Externalized |
browser | @format:documents/browser | CDN via setAssetsUrl | All bundled |
worker | @format:documents/edge | CDN via setAssetsUrl | All bundled |
With a single target, @format:documents and the suffixed path both work. With multiple targets, use the suffixed paths to import a specific target.
Per-target options override shared defaults:
export default withFormat({
inlineRemoteCss: true,
targets: [
{ target: 'node', bundle: ['date-fns'] },
{ target: 'browser', assets: 'dynamic', inlineRemoteCss: false }
]
})(nextConfig)All targets compile in parallel. Results are cached, so subsequent dev server starts skip compilation if nothing changed.
Browser target
Import from @format:documents/browser in client components:
'use client'
import { invoice } from '@format:documents/browser'
import { FormatClient } from '@format.dev/client'
invoice.setAssetsUrl('https://cdn.example.com/invoice/assets.zip')
const doc = await invoice.render({ customerName: 'Ada Lovelace' })
const format = new FormatClient()
const response = await format.pdf(doc)
const blob = await response.blob()Browser targets can't read a local assets.zip. Either host the ZIP and point at it with setAssetsUrl (works in either asset mode), or use assets: 'dynamic' to build the ZIP at render time.
Edge target
Import from @format:documents/edge in edge route handlers:
export const runtime = 'edge'
import { invoice } from '@format:documents/edge'
import { FormatClient } from '@format.dev/client'
export async function GET() {
invoice.setAssetsUrl(process.env.ASSETS_URL!)
const doc = await invoice.render({ customerName: 'Ada Lovelace' })
const format = new FormatClient()
const response = await format.pdf(doc)
return new Response(response.body, {
headers: { 'Content-Type': 'application/pdf' }
})
}Edge bundles are fully self-contained with all dependencies bundled in.
Deployment
withFormat automatically configures Next.js for production. It adds your compiled output to serverExternalPackages and outputFileTracingIncludes, so the _generated directory is included in standalone builds.
No manual configuration needed. Run next build and deploy.
Read the deployment guide for more on assets, Docker, and CI/CD.
Options
FormatNextPluginOptions
| Name | Description |
|---|---|
| Compile to multiple targets with per-target options. | |
Default asset mode for all targets. Overridden by target-level assets. | |
Inline remote CSS for all targets. Overridden by target-level inlineRemoteCss. | |
Whether to validate against schema for all targets. Overridden by target-level validateSchema. | |
Name for the compiled bundle and virtual module prefix.
Sets the import alias to @format:<bundleName>. | |
| Clear the output directory before recompiling. | |
| Override the output directory. | |
Explicit path to format.config.*. |
targets
FormatTargetOptions[][{ target: 'node' }]assets
assets. See --assets for more information on the available modes."static" | "dynamic" | "none""static"inlineRemoteCss
inlineRemoteCss. See --inline-remote-css for more information.booleanfalsevalidateSchema
validateSchema.booleantruebundleName
@format:<bundleName>.string"documents"clean
booleantrueoutDir
string<rootDir>/_generatedconfigPath
format.config.*.stringFormatTargetOptions
| Name | Description |
|---|---|
targetRequired | The output target. |
assets | Asset mode for this target. See --assets for more information on the available modes. |
inlineRemoteCss | Whether to inline remote CSS for this target. See --inline-remote-css for more information. |
validateSchema | Whether to validate against schema for this target. |
bundle | Packages to force-bundle (node target).
The node target externalizes all deps by default. Use this for
packages that won't be in node_modules at runtime. See --bundle for more information. |
external | Packages to externalize (browser/worker targets).
These targets bundle all deps by default. Use this for
packages your runtime already provides. See --external for more information. |