Using @contember/admin inside Next.js app

By integrating Contember Admin into your Next.js app, you can efficiently manage and update data, delivering seamless user experiences. However, there are a few considerations to keep in mind. Contember Admin is client-side, so server-side rendering (SSR) needs to be disabled for that component. Additionally, a public token is required to access Contember Admin. In this guide, we'll walk through the necessary steps to set up Contember Admin within Next.js, overcoming these limitations.
Also keep in mind, that Contember CSS may collide with other CSS frameworks, like Tailwind.

next.config.js

In next config, we need to transpile the @contember/admin package. Also, we disable react strict mode, as there are currently some issues with some components.
const withTM = require('next-transpile-modules')(['@contember/admin'])
/** @type {import('next').NextConfig} */
const nextConfig = {
reactStrictMode: false,
}
module.exports = withTM(nextConfig)

components/SubmitGist.tsx

Here is a sample component used in Next.js page. We must first setup some contexts.
import { ContemberClient, CreateScope, I18nProvider, PersistButton, RichTextField, TextField, ToasterProvider } from '@contember/admin'
export const SubmitGist = () => {
return (
<I18nProvider localeCode={undefined} dictionaries={undefined}>
<ToasterProvider>
<ContemberClient
apiBaseUrl={process.env.NEXT_PUBLIC_CONTEMBER_BASE_URL!}
project={process.env.NEXT_PUBLIC_CONTEMBER_PROJECT}
stage={process.env.NEXT_PUBLIC_CONTEMBER_STAGE}
sessionToken={process.env.NEXT_PUBLIC_CONTEMBER_TOKEN}
>
<CreateScope entity={'Gist'}>
<TextField field="title" label="Gist title" required />
<RichTextField field="contentJson" label="Gist description" />
<PersistButton />
</CreateScope>
</ContemberClient>
</ToasterProvider>
</I18nProvider>
)
}

pages/submit.tsx

To force client side rendering, we need to wrap our Contember component like this:
import dynamic from 'next/dynamic'
const NoSsrWrapper = dynamic(
() => import('../components/SubmitGist').then(it => it.SubmitGist),
{ ssr: false },
)
export default () => {
return <>
<NoSsrWrapper />
</>
}

pages/_app.tsx

To include @contember/admin CSS, add them to Next application entrypoint
// ....
import '@contember/admin/style.css'
// ....

style/global.css

Here are some, but not all, fixes to solve compatibility issues with a tailwind.
body, html {
display: initial;
min-width: initial;
}
* {
border-color: initial;
}
.cui-textarea-input, .cui-text-input {
padding-left: var(--cui-padding-horizontal);
padding-right: var(--cui-padding-horizontal);
}
.gists-submit .cui-repeater-item-container-label {
display: none;
}
.cui-textarea-input {
padding-bottom: var(--cui-text-area-padding-vertical);
padding-top: var(--cui-text-area-padding-vertical);
}

.env.local

Sample env file. Keep in mind that the token will be exposed publicly, so make sure your ACL is correct
NEXT_PUBLIC_CONTEMBER_BASE_URL=http://localhost:1481
NEXT_PUBLIC_CONTEMBER_PROJECT=my-project
NEXT_PUBLIC_CONTEMBER_STAGE=live
NEXT_PUBLIC_CONTEMBER_TOKEN=0000000000000000000000000000000000000000