Next.js
Next.js is an open source React-based framework created by Vercel. Our CX Framework for Next.js supports Static Site Generation (with Incremental Static Regeneration) as well as Server Side Rendering.
Consider using our Quickstart to get a ready-to-use setup. You can also go through the steps below to understand how our CX Framework works with Next.js.
The following steps focus on using Incremental Static Regeneration connected to the Delivery API which is our recommended approach.
Setup
- Install the Atama packages with your preferred package manager:
npm i @atamaco/nextjs @atamaco/fetcher-atama @atamaco/renderer-react
- Add two environment variables for the integration. Add them to your
.env
or.env.local
file:
ATAMA_API_KEY=
ATAMA_WORKSPACE_ID=
See here for how to generate these values.
Don't forget to add the environment variables when deploying to your hosting platform (e.g. Netlify, Vercel, etc.).
Data
The following section shows how you can render data from your data business capabilities.
- Create a
lib/atama.ts
file
import { AtamaRenderer } from '@atamaco/renderer-react';
import { FetcherAtama } from '@atamaco/fetcher-atama';
import { Hero } from '../components/atama/hero';
import { LandingPage } from '../layouts/landing-page';
export const fetcher = new FetcherAtama({
apiKey: process.env.ATAMA_API_KEY as string,
workspaceId: process.env.ATAMA_WORKSPACE_ID as string,
});
export interface MetaData {
seoTitle: string;
seoDescription: string;
}
export const LAYOUTS = {
LandingPage,
};
export const COMPONENTS = {
Hero,
};
- and then create the
LandingPage
import type { ReactNode } from 'react';
import { forwardRef } from 'react';
interface LandingPageProps {
main: ReactNode;
}
export const LandingPage = forwardRef<HTMLDivElement, LandingPageProps>(
({ main }, ref) => (
<div ref={ref}>
<div data-atama-placement="Main">{main}</div>
</div>
),
);
LandingPage.displayName = 'LandingPage';
- ... as well as the
Hero
component.
import type { AtamaComponentProps } from '@atamaco/renderer-react';
export interface HeroProps {
title: string;
subtitle: string;
atama?: AtamaComponentProps;
}
export function Hero({
title,
subtitle,
atama,
}: HeroProps) {
return (
<section {...atama}>
<h1>{title}</h1>
<h2>{subtitle}</h2>
</section>
);
}
Now that we have the basic setup done we can create an actual page that renders experiences:
Create a pages/[...slug].tsx
file. This is creating a dynamic catch all route.
import type { MetaData } from '../lib/atama';
import type { NextPage } from 'next';
import type { AtamaProps } from '@atamaco/nextjs';
import { getStaticPropsFactory, getStaticPathsFactory } from '@atamaco/nextjs';
import Head from 'next/head';
import { AtamaRenderer } from '@atamaco/renderer-react';
import { COMPONENTS, LAYOUTS, fetcher } from '../lib/atama';
export const ContentPage: NextPage<AtamaProps<MetaData>> = ({ data }) => (
<>
<Head>
<title>
{typeof data?.meta?.seoTitle === 'string' ? data?.meta?.seoTitle : ''}
</title>
<meta
name="description"
content={
typeof data?.meta?.seoDescription === 'string'
? data?.meta?.seoDescription
: ''
}
/>
</Head>
<main>
{data && (
<AtamaRenderer layouts={LAYOUTS} components={COMPONENTS} data={data} />
)}
</main>
</>
);
export const getStaticPaths = getStaticPathsFactory(fetcher);
export const getStaticProps = getStaticPropsFactory(fetcher);
Actions
- Create an API route
/api/actions/[action].ts
:
import { createActionHandler } from '@atamaco/nextjs';
import { fetcher } from '../../../lib/atama';
const handler = createActionHandler(fetcher);
export default handler;
You can also choose a different path for the API route if this conflicts with your existing setup. If you choose a different path make sure to pass the path to the action
method as apiRoutePath
.
- Create a
newsletter-signup.tsx
component.
This example is using @tanstack/react-query so make sure to install it with: npm i @tanstack/react-query
. Make sure to add the QueryClientProvider
to your app. See the TanStack Query Quick Start docs.
import type {
AtamaComponentProps,
AtamaActions
} from '@atamaco/renderer-react';
export interface NewsletterSignupProps {
title: string;
atama?: AtamaComponentProps;
actions?: AtamaActions;
}
export function NewsletterSignup({
title,
atama,
actions,
}: NewsletterSignupProps) {
const formRef = useRef<HTMLFormElement>(null);
const actionId = actions?.find(
(_action) => _action.key === 'newsletter-signup',
)?.actionId;
if (!actionId) {
throw new Error('No actionId found for newsletter-signup action');
}
const { mutate, data, isLoading } = useMutation(
action<Array<null | { success: true }>>({
actionId,
slug: '/index',
}),
);
const onSubmit = (event) => {
event.preventDefault();
};
return (
<section {...atama}>
<form
onSubmit={onSubmit}
ref={formRef}
>
<h3>{title}</h3>
<input
type="email"
name="email"
placeholder="Type your email"
required
/>
<button disabled={isLoading}>Subscribe</button>
</form>
</section>
);
}
- add the
NewsletterSignup
component to thecomponents
object inlib/atama.ts
.