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

# Setup

> Build your own visual editor in 5 minutes

In this guide, we will show you how to easily create a working visual editor with Mirin Embeds.

Embeds work with any React framework and any Javascript backend. For simplicity, we'll be using Next.js 15 with React Server Components in this guide.

## Create a new Embed

Login to your [account](#) on Mirin and create a new Embed. You will then need to generate a new Private Key that you will use to setup the SDKs later in this guide.

<video controls className="w-full" src="https://static.mirin.app/public/docs/setup/mirin-private-key.mp4" />

## Project setup

1. Install Next.js

```sh theme={null}
npx create-next-app@latest
```

2. Install SDK

After you have setup a fresh Next.js app, `cd` into the project directory and install the following packages:

```sh theme={null}
npm install --save @mirinhq/embed @mirinhq/admin
```

3. Add your private key

In your Next.js project directory, create a new `.env.local` file and add your Embed's private key.

```
MIRIN_EMBED_PRIVATE_KEY=...
```

4. Setup the Admin client

Then, we will need to setup a new admin `Client` which we'll use to perform priviledged operations such as authenticating our users into our new embedded Mirin editor.

Typically, this will live in your backend and you would create API endpoints to interface with the `Client`. In Next.js 15, we can use server actions without having to create a separate backend:

```tsx lib/server.tsx theme={null}
"use server";

import { Client } from "@mirinhq/admin";

export const client = new Client({
  apiKey: process.env["MIRIN_EMBED_PRIVATE_KEY"],
});
```

## Creating Documents

Before we can actually launch the editor, we need to first have some Documents that we can edit.

Let's now create a simple Dashboard page where we can:-

1. List all Documents associated with our Embed
2. Let our user to create a new Document

To do that, we'll create 2 server actions respectively:

```tsx lib/server.tsx theme={null}
'use server';

import { Client } from '@mirinhq/admin';
import { revalidatePath } from 'next/cache';

const client = new Client({ ... });

export const getDocuments = async () => {
  return await client.getDocuments();
};

export const createDocument = async (
  ...args: Parameters<typeof client.createDocument>
) => {
  await client.createDocument(...args);
  revalidatePath('/');
};
```

For our frontend, we'll just create a really simple UI to list our user documents and provide a button to create a new document:

<CodeGroup>
  ```tsx app/dashboard/page.tsx theme={null}
  import { getDocuments } from '@/lib/server';
  import Link from 'next/link';

  import { CreateDocumentButton } from './CreateDocumentButton';

  export default async function Dashboard() {
    const { documents } = await getDocuments();

    return (
      <div className="flex flex-col gap-5 p-10">
        <div className="flex items-center gap-5">
          <h2 className="text-xl">Documents</h2>
          <CreateDocumentButton />
        </div>
        {documents.length === 0 ? (
          <p>No documents</p>
        ) : (
          <ul className="flex flex-col">
            {documents.map((document) => {
              return (
                <li className="w-full" key={document.id}>
                  <Link href={`/editor/${document.id}`}>{document.id}</Link>
                </li>
              );
            })}
          </ul>
        )}
      </div>
    );
  }
  ```

  ```tsx app/dashboard/CreateDocumentButton.tsx theme={null}

  'use client';

  import { createDocument } from '@/lib/server';

  export function CreateDocumentButton() {
    const handleOnClick = async function () {
      await createDocument({
        title: 'A new doc',
      });
    }

    return (
      <buttono onClick={handleOnClick} className="rounded-md px-2.5 py-1 text-sm bg-neutral-100 text-neutral-700">
        Create new Document
      </button>
    );
  }

  ```
</CodeGroup>

In a real app, you would ideally only want to list documents that is associated with a specific user. Check out the [Documents](#) page for more information.

## Setup the editor

Onto the most exciting part - actually integrating the editor.

The editor requires a valid session token in order to authenticate the user and to fetch the correct user document to display in the editor.

We can generate this token with our admin `Client` - so let's create a new server action to do just that:

```tsx lib/server.tsx theme={null}
'use server';

import { Client } from '@mirinhq/admin';
import { revalidatePath } from 'next/cache';

const client = new Client({ ... });

export const authenticate = async (documentId: string) => {
  const res = await client.authorize({
    documentId,
  });

  return res.token;
};
```

Here, we're simply taking a `documentId` argument which our page on the frontend will pass, and we're generating a valid token that effectively grants the user access to the edit the document on our Editor.

In a real app, you probably want to add additional logic here - e.g. to check if your user has the correct permissions to edit a given document.

> The `client.authorize()` method accepts a context object, by default - this object is used to specify the `documentId` to grant the user access to edit.

Finally, let's create a new page in our project where we'll render our editor.

```tsx app/editor/[documentId]/page.tsx theme={null}

'use client';

import * as React from 'react';
import { useParams } from 'next/navigation';

import { Editor } from '@mirinhq/embed';
import '@mirinhq/embed/dist/index.css';

import { authenticate } from '@/lib/server';

export default function EditorPage() {
  const { documentId } = useParams<{ documentId: string }>();

  const  handleOnAuthenticate = async function () {
    // 1. Get the token from our server action via client.authorize()
    const token = await authenticate(documentId);

    // 2: Return the token and documentId to the editor
    return {
      token,
      documentId,
    };
  }

  return (
    <Editor authenticate={handleOnAuthenticate} />
  );
}

```

## That's it, really 🎉

Just like that - you now have a fully functioning visual editor.

<video controls className="w-full" src="https://static.mirin.app/public/docs/setup/setup-demo.mp4" />
