The fastest way to get started with Makeswift on a new Next.js project is to follow the quickstart guide. If you have an existing Next.js application or want to set things up yourself, continue with the rest of this guide.

System requirements

  • Node.js 18.17 or a later version.
  • macOS, Windows (including WSL), and Linux are supported.

Geting started


Open your Next.js project

First, open your Next.js project. If you don’t already have one, head over to the Next.js documentation to get one set up. If you do have one, please verify you are using Next.js 13.4 or a later version.


Install dependencies

Install the @makeswift/runtime package. This package contains all of the necessary code to integrate Makeswift into your Next.js app.

npm install @makeswift/runtime

Add API key to environment variables

Requesting data through the Makeswift client requires a site API key from Makeswift. In the Makeswift builder, go to Settings > Host and copy the API key for the site.

How to get the site API key

Once the API key is in your clipboard, open your .env.local file and paste the snippet below.


Add Makeswift runtime and client

Create files for the Makeswift runtime and client.

import { ReactRuntime } from "@makeswift/runtime/react"

export const runtime = new ReactRuntime()

Add the Makeswift API handler

Similar to NextAuth.js, Makeswift uses an API handler to communicate with your Next.js app. Create the file pages/api/makeswift/[...makeswift].ts.

It is important this file has that exact name and path. The extension can be .js or .ts.

import { MakeswiftApiHandler } from "@makeswift/runtime/next/server"
import { strict } from "assert"

import { runtime } from "@/makeswift/runtime"

strict(process.env.MAKESWIFT_SITE_API_KEY, "MAKESWIFT_SITE_API_KEY is required")

export default MakeswiftApiHandler(process.env.MAKESWIFT_SITE_API_KEY, {

This API route adds support for preview mode, on-demand revalidation, and other features that make Makeswift work seamlessly with your Next.js app.


Add the Next.js plugin

Next.js plugins are configured in the project’s next.config.js file by wrapping nextConfig. The Makeswift Next.js plugin whitelists Makeswift image domains and sets up rewrites to enable preview mode in the Makeswift builder.

import createWithMakeswift from "@makeswift/runtime/next/plugin"

const withMakeswift = createWithMakeswift()

/** @type {import('next').NextConfig} */
const nextConfig = {
  // your existing next config

export default withMakeswift(nextConfig)

Set up a custom Document

Create a custom Document named _document.ts and export Document from @makeswift/runtime/next. The Makeswift custom Document handles styles during server-side rendering and using Preview Mode when opening your pages in the Makeswift builder.

export { Document as default } from "@makeswift/runtime/next"

If you already have a _document.ts, you can extend the Document from @makeswift/runtime/next instead. You must render the <PreviewModeScript> component inside <Head>. Without <PreviewModeScript> the Makeswift builder will always show your published page instead of the draft version.

import { Html, Head, Main, NextScript } from "next/document"
import { Document, PreviewModeScript } from "@makeswift/runtime/next"

export default class MyDocument extends Document {
  render() {
    return (
          <PreviewModeScript isPreview={this.props.__NEXT_DATA__.isPreview} />
          <Main />
          <NextScript />

Register components with Makeswift

Create a file for registered components called makeswift/components.tsx. In this example, only one component is registered. However, as you register more components, we recommend creating separate files for each component and rolling up the imports in the makeswift/components.ts file. Learn more about registering components.

import { Style } from "@makeswift/runtime/controls"

import { runtime } from "./runtime"

function HelloWorld(props) {
  return <p {...props}>Hello, world!</p>

runtime.registerComponent(HelloWorld, {
  type: "hello-world",
  label: "Custom / Hello, world!",
  props: {
    className: Style(),

Provide the runtime to Custom App

If you don’t have a Custom App you’ll need to create one. Then wrap your Custom App with the Makeswift ReactRuntimeProvider component.

import type { AppProps } from "next/app"
import { ReactRuntimeProvider } from "@makeswift/runtime/next"

import { runtime } from "@/makeswift/runtime"
import "@/makeswift/components"

export default function App({ Component, pageProps }: AppProps) {
  return (
    <ReactRuntimeProvider runtime={runtime}>
      <Component {...pageProps} />

Add a route for Makeswift pages

Create an optional catch-all route named [[...path]].tsx. You will use this route to fetch page snapshots from the Makeswift client and render them using the Page component.

import { GetStaticPaths, GetStaticProps, InferGetStaticPropsType } from "next"

import {
  Page as MakeswiftPage,
} from "@makeswift/runtime/next"

import { client } from "@/makeswift/client"

export const getStaticPaths = (async () => {
  const pages = await client.getPages()

  return {
    paths: => ({
      params: {
        path: page.path.split("/").filter((segment) => segment !== ""),
    fallback: "blocking",
}) satisfies GetStaticPaths

export const getStaticProps = (async ({ params, previewData }) => {
  if (params == null) return { notFound: true }

  const path = Array.isArray(params.path) ? "/" + params.path.join("/") : "/"

  const snapshot = await client.getPageSnapshot(path, {
    siteVersion: Makeswift.getSiteVersion(previewData),

  if (snapshot == null) return { notFound: true }

  return { props: { snapshot } }
}) satisfies GetStaticProps<PageProps>

export default function Page({
}: InferGetStaticPropsType<typeof getStaticProps>) {
  return <MakeswiftPage snapshot={snapshot} />

Important notes:

  1. If you already have an index.tsx file, you will need to name the file [...path].tsx instead of [[...path]].tsx. For more information about the differences between catch-all and optional catch-all segments, refer to the Next.js Catch-all segments documentation.

  2. The filename defines the path param. For example, if the filename is [[...slug]].tsx instead of [[...path]].tsx, then the param name is slug. Because this is an optional catch-all route, there are no params when visiting the index (i.e., /) path. The path param defaults to an empty array.

  3. fallback: 'blocking' is used here so that your Next.js app doesn’t need to be re-deployed whenever a new Makeswift page is created.

With this setup, your pages will be rendered using incremental static regeneration. A revalidate field isn’t added to the returned value of getStaticProps because Makeswift pages are automatically revalidated using on-demand revalidation by leveraging the Makeswift API handler.


Start the local dev server

Run the local development script. This will start the Next.js app at http://localhost:3000.

npm run dev

If port 3000 is already in use, Next.js will try port 3001, then 3002, and so forth until it finds an unused port.

Take note of this port for the next step.

Add your app's URL to Makeswift

Finally, open the Makeswift builder, navigate to Settings > Host, and add your app’s URL. If you haven’t changed anything in the example and the server is running on port 3000, the app’s URL should be http://localhost:3000.

How to update the host url

When you’re ready to deploy, set up a separate site and use your deployment URL instead of http://localhost:3000. You can keep this site for local development.


Start building

Great job! You should be able to create a page in Makeswift and start dropping in registered components from the left toolbar.

Hello world component registered