> ## Documentation Index
> Fetch the complete documentation index at: https://docs.makeswift.com/llms.txt
> Use this file to discover all available pages before exploring further.

# App Router

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

<Note>
  This installation guide is built for [Next.js
  15](https://nextjs.org/blog/next-15) but also works with [Next.js
  13](https://nextjs.org/blog/next-13) and [Next.js
  14](https://nextjs.org/blog/next-14).
</Note>

## System requirements

* [Node.js 20.19](https://nodejs.org/en) or a later version.
* macOS, Windows (including WSL), and Linux are supported.

If you don't already have a Next.js project, head over to
the [Next.js](https://nextjs.org/docs/getting-started/installation)
documentation to get one set up. If you do have one, please verify you are using [Next.js 13.4](https://nextjs.org/blog/next-13-4) or a later version that is using [App Router](https://nextjs.org/docs/getting-started/installation).

<Info>
  If you are not using App Router, here's how to [incrementally
  adopt](https://nextjs.org/docs/app/building-your-application/upgrading/app-router-migration)
  it.
</Info>

This code in this guide assumes you are using a `src` directory and have the following path aliases configured.

```json tsconfig.json theme={null}
{
  "compilerOptions": {
    "baseUrl": ".",
    "paths": {
      "@/*": ["src/*"]
    }
  }
}
```

If your setup varies from this, please adjust the code snippets appropriately.

## Install dependencies

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

```bash theme={null}
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.

<Frame>
  <video autoPlay muted loop playsInline controls title="Copy site API key" className="w-full aspect-video" src="https://mintcdn.com/makeswift/AOijlpVp0npNQ8wB/images/installation/copy-site-api-key.mp4?fit=max&auto=format&n=AOijlpVp0npNQ8wB&q=85&s=8dcad9a70e8ff30f6e3b4129c123c8cc" data-path="images/installation/copy-site-api-key.mp4" />
</Frame>

Once the API key is in your clipboard, open your [`.env.local`](https://nextjs.org/docs/pages/building-your-application/configuring/environment-variables) file and paste the snippet below.

```sh theme={null}
MAKESWIFT_SITE_API_KEY=paste-your-api-key-here
```

## Add Makeswift runtime

Create the Makeswift [runtime](/developer/reference/runtime/constructor) file in `src/makeswift`.

```ts src/makeswift/runtime.ts theme={null}
import { ReactRuntime } from "@makeswift/runtime/next";

export const runtime = new ReactRuntime();
```

## Add Makeswift client

Create the Makeswift [client](/developer/reference/client/constructor) file in `src/makeswift`.

```ts src/makeswift/client.ts theme={null}
import { Makeswift } from "@makeswift/runtime/next";
import { strict } from "assert";

import { runtime } from "./runtime";

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

export const client = new Makeswift(process.env.MAKESWIFT_SITE_API_KEY, {
  runtime,
});
```

## 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 draft mode in the Makeswift builder.

<CodeGroup>
  ```js next.config.ts theme={null}
  import createWithMakeswift from "@makeswift/runtime/next/plugin";

  const withMakeswift = createWithMakeswift();

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

  export default withMakeswift(nextConfig);
  ```

  ```js next.config.js theme={null}
  const createWithMakeswift = require("@makeswift/runtime/next/plugin");

  const withMakeswift = createWithMakeswift();

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

  export default withMakeswift(nextConfig);
  ```
</CodeGroup>

## Register components with Makeswift

Create a file for registered components called `src/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 `src/makeswift/components.tsx` file. Learn more about [registering components](/developer/reference/runtime/register-component).

```tsx src/makeswift/components.tsx theme={null}
import { runtime } from "@/makeswift/runtime";
import { Style } from "@makeswift/runtime/controls";

function HelloWorld({ className }: { className: string }) {
  return <p className={className}>Hello, world!</p>;
}

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

To ensure these your components become available in Makeswift, this `components.tsx` file must be imported in three different places:

1. In the [Makeswift Provider](#create-makeswift-provider-component)
2. In root [layout file](#update-the-root-layout)
3. In the [Makeswift API Handler](#add-the-makeswift-api-handler)

You'll do this in the next few steps.

## Create Makeswift provider component

Create a client component for the Makeswift providers.

```tsx src/makeswift/provider.tsx theme={null}
"use client";

import { runtime } from "@/makeswift/runtime";
import { type SiteVersion } from "@makeswift/runtime/next";
import {
  ReactRuntimeProvider,
  RootStyleRegistry,
} from "@makeswift/runtime/next";
import "@/makeswift/components";

export function MakeswiftProvider({
  children,
  siteVersion,
}: {
  children: React.ReactNode;
  siteVersion: SiteVersion | null;
}) {
  return (
    <ReactRuntimeProvider siteVersion={siteVersion} runtime={runtime}>
      <RootStyleRegistry>{children}</RootStyleRegistry>
    </ReactRuntimeProvider>
  );
}
```

<Note>
  Make sure to import your `src/makeswift/components.tsx` file here to ensure
  your registered components are available in the builder.
</Note>

## Update the root layout

In your root layout, wrap your app with the `MakeswiftProvider` component created in the last step and import the registered components.

```tsx src/app/layout.tsx theme={null}
import type { Metadata } from "next";
import { draftMode } from "next/headers";
import { Inter } from "next/font/google";
import { getSiteVersion } from "@makeswift/runtime/next/server"
import { MakeswiftProvider } from "@/makeswift/provider";
import "@/makeswift/components";
import "./globals.css";

const inter = Inter({ subsets: ["latin"] });

export const metadata: Metadata = {
  title: "Create Next App",
  description: "Generated by create next app",
};

export default async function RootLayout({
  children,
}: Readonly<{
  children: React.ReactNode;
}>) {
  return (
    <html lang="en">
      <body className={inter.className}>
        <MakeswiftProvider siteVersion={await getSiteVersion()}>
          {children}
        </MakeswiftProvider>
      </body>
    </html>
  );
}
```

## Add a route for Makeswift pages

Create an [optional catch-all route](https://nextjs.org/docs/app/building-your-application/routing/dynamic-routes#optional-catch-all-segments) named `[[...path]]`.
This catch-all route will fetch page data from `Makeswift` and pass it to be rendered in the `Page` component.

```tsx src/app/[[...path]]/page.tsx theme={null}
import { getSiteVersion } from "@makeswift/runtime/next/server";
import { notFound } from "next/navigation";
import { Page as MakeswiftPage } from "@makeswift/runtime/next";

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

export async function generateStaticParams() {
  const pages = await client.getPages().toArray();

  return pages.map((page) => ({
    path: page.path.split("/").filter((segment) => segment !== ""),
  }));
}

export default async function Page({
  params,
}: {
  params: Promise<{ path?: string[] }>;
}) {
  const path = "/" + ((await params)?.path ?? []).join("/");
  const snapshot = await client.getPageSnapshot(path, {
    siteVersion: getSiteVersion(),
  });

  if (snapshot == null) return notFound();

  return <MakeswiftPage snapshot={snapshot} />;
}
```

Delete the root page component `src/app/page.tsx` file to ensure that all pages (including the home page) are managed by Makeswift.

<Note>
  Optional catch-all routes match the parent route which, in this case, would be
  the root page `/`. If you wanted to have a hard-coded home page (not managed
  by Makeswift), you could use a (non-optional) catch-all route which does not
  match the parent route and uses single brackets instead (ex. `[...path]`).
</Note>

## Add the Makeswift API handler

Similar to [NextAuth.js](https://next-auth.js.org/), Makeswift uses an API handler to communicate with your Next.js app. Create the file `src/app/api/makeswift/[...makeswift]/route.ts`.

<Note>
  It is important this file has that exact name and path. The extension can be
  `.js` or `.ts`.
</Note>

```tsx src/app/api/makeswift/[...makeswift]/route.ts theme={null}
import { MakeswiftApiHandler } from "@makeswift/runtime/next/server";
import { strict } from "assert";

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

// make custom components' data available for introspection
import "@/makeswift/components";

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

const handler = MakeswiftApiHandler(process.env.MAKESWIFT_SITE_API_KEY, {
  runtime,
});

export { handler as GET, handler as POST, handler as OPTIONS };
```

This API route adds support for
[Draft Mode](https://nextjs.org/docs/app/building-your-application/configuring/draft-mode),
[on-demand revalidation](https://nextjs.org/docs/app/building-your-application/data-fetching/incremental-static-regeneration#on-demand-revalidation-with-revalidatetag),
and other features that make Makeswift work seamlessly with your Next.js app.

## Start the local dev server

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

```bash theme={null}
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.

<Note>Take note of this port for the next step.</Note>

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

<Frame>
  <video autoPlay muted loop playsInline controls title="Hello world component" className="w-full aspect-video" src="https://mintcdn.com/makeswift/AOijlpVp0npNQ8wB/images/installation/update-host-url.mp4?fit=max&auto=format&n=AOijlpVp0npNQ8wB&q=85&s=20c070caeb610c2d5fa5e29b0c993d9a" data-path="images/installation/update-host-url.mp4" />
</Frame>

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.

<Frame>
  <img src="https://mintcdn.com/makeswift/AOijlpVp0npNQ8wB/images/installation/hello-world-registered.jpg?fit=max&auto=format&n=AOijlpVp0npNQ8wB&q=85&s=ab28877ce188faa8f700f3e1f39e7a39" alt="Hello world component registered" width="1824" height="1026" data-path="images/installation/hello-world-registered.jpg" />
</Frame>

## Next Steps

Here are a couple of ideas for what you might want to do next.

<CardGroup cols={3}>
  <Card title="Concepts" href="/developer/concepts">
    Learn the core concepts behind how Makeswift works.
  </Card>

  <Card title="Custom components" href="/developer/guides/how-to/built-in-components">
    Build custom components for use in the Visual Builder.
  </Card>

  <Card title="Localization" href="/developer/app-router/localization">
    Translate your content into multiple languages.
  </Card>
</CardGroup>
