This is a starter template for building a headless site powered by Acquia CMS and Next.js.
This project is built on the following technologies:
- Drupal core
- Acquia CMS (Drupal distribution)
- Next.js (React front-end framework)
- Next.js for Drupal (Tools for integrating Next.js with Drupal)
- Drupal JSON-API Params (Tools for generating JSON:API queries)
- Tailwind CSS (Styling library)
- TypeScript
Start by creating a new Acquia CMS project:
(If you have an existing Acquia CMS project, you can skip this step)
composer create-project acquia/drupal-recommended-project acms-demo
cd acms-demo && ./vendor/bin/acms acms:install
From the wizard, select the acquia_cms_headless starter kit and proceed to install Acquia CMS. We recommend using the default Content Model and the demo content so you can quickly see the content coming through your Next.js application.
Note: next-acms requires Acquia CMS 2.0 or later.
Login to Acquia CMS and go to Tour > Get started. From the Headless section on the CMS Dashboard, select the Next.js starter kit and click "Save". Acquia CMS should print a message with your Node environment variables. Copy these and proceed to the next step.
For more information on Acquia CMS setup please see the tutorial.
Run the following command to create a new Next.js project:
npx create-next-app -e https://github.com/acquia/next-acms/tree/main/starters/basic-starter
This will create a new starter project. See project structure.
To connect the Next.js site to Drupal, we use environment variables.
- Copy
.env.example
to.env.local
. - Paste in the credentials generated by Acquia CMS and save.
To start the Next.js development server, run yarn dev
. This starts the development server on http://localhost:3000
.
Visit http://localhost:3000 to view the headless site.
This project is a monorepo structure using yarn workspaces. This project has a packages
directory for next-acms and a starters
directory for next-acms starter kits.
starters/basic-starter
has a dependency on packages/next-acms
which currently has a dependency on next-drupal
.
.
|── node_modules
|── packages
|── next-acms
| |── node_modules
| |── package.json
|── starters
|── basic-starter
├── components
│ ├── layout.tsx
│ ├── media--image.tsx
│ ├── menu--footer.tsx
│ ├── menu--main.tsx
│ ├── node--article.tsx
│ └── node--page.tsx
├── lib
│ └── format-date.ts
├── node_modules
├── pages
│ ├── api
│ │ ├── exit-preview.tsx
│ │ ├── preview.tsx
│ │ └── revalidate.ts
│ ├── _app.tsx
│ ├── [...slug].tsx
│ ├── articles.tsx
│ └── index.tsx
├── public
│ ├── favicon.ico
│ ├── logo.png
│ └── robots.txt
├── styles
│ └── globals.css
├── .env.local
├── next.config.js
├── package.json
├── tailwind.config.js
└── tsconfig.json
|── .gitignore
|── package.json
Path | Description |
---|---|
components |
Place your React components here. |
lib |
For utility or helper functions. |
pages |
Learn more about the pages directory here |
public |
For static files. |
styles |
Directory for CSS files. |
.env.local |
File for local environment variables. |
next.config.js |
Configuration file for Next.js. |
tailwind.config.js |
Configuration file for Tailwind CSS. |
The starter ships with static routes for building collection of content: pages/articles
and an entry point, [...slug].tsx
, for entity routes.
The [...slug].tsx
route is called a catch-all route.
When you create an entity on Drupal, and visit the route on your headless site, this is the file that handles data fetching and rendering for the entity.
You can read more about routing in Next.js on the official docs.
To enable the inline content preview inside Drupal, we need to configure a site resolver for the content type.
A site resolver tells Drupal how to resolve the preview URL for an entity.
- Visit /admin/config/services/next/entity-types
- Click Configure entity type
- Select Article from the the entity type list
- Select Site selector as the Site resolver
- Select the Next.js site under Next.js sites
- Click Save
If you visit an article from the Drupal administration, you should now see an inline preview inside an iframe.
Repeat the same steps for other content types.
To build pages from Drupal content, data is fetch in getStaticProps
and passed to the component.
See the documentation on data fetching in next-drupal.
To create headless pages for a new content type:
- Start by creating the content type, say
News
, on Drupal.- Define the fields:
title
,field_teaser
andbody
. - Add a path alias for the content type:
news/[node:title]
.
- Define the fields:
- On the Next.js site, edit
[...slug].tsx
to fetch news from Drupal. - Add
node--news
toCONTENT_TYPES
:
// List of all the entity types handled by this route.
const CONTENT_TYPES = [
"node--page",
"node--article",
"node--event",
"node--person",
"node--place",
+ "node--news",
]
- In
getStaticProps
, build the query for fetching thenode--news
with the fields:
+ if (type === "node--news") {
+ params
+ .addFields("node--news", ["title", "path", "field_teaser", "body])
+ }
- Add a news on Drupal and visit the path on your Next.js site:
http://localhost:3000/news/title-of-news
. - If you add a
console.log(node)
in theNodePage
component you should see thenode--news
data.
export default function NodePage({ node, menus }: NodePageProps) {
if (!node) return null
+ console.log(node)
- You can now use this
node--news
object to create the<NodeNews />
component.
export default function NodePage({ node, menus }: NodePageProps) {
if (!node) return null
return (
<Layout title={node.title} menus={menus}>
{node.type === "node--page" && <NodeBasicPage node={node} />}
{node.type === "node--article" && <NodeArticle node={node} />}
{node.type === "node--event" && <NodeEvent node={node} />}
{node.type === "node--person" && <NodePerson node={node} />}
{node.type === "node--place" && <NodePlace node={node} />}
+ {node.type === "node--news" && <NodeNews node={node} />}
</Layout>
)
}
If you believe you have found a security vulnerability in an Acquia product or service, we encourage you to responsibly disclose this and not open a public issue. We will investigate all legitimate reports. Please email the details to our security team at [email protected].