Share this article:

Blog
Jan 13, 20237 min read

Headless Drupal with Next.js - simple example walkthrough

Headless Drupal with Next.js - simple example walkthrough

Drupal and Next.js

Drupal is a popular and powerful content management system (CMS) that makes it easy to create editorial workflows and interfaces that meet your specific needs. It is modular based and is highly customisable with a sisable community and technological maturity and could be used to create full stack web applications. The trend recently, in web development in general, and consequently in Drupal development is to use the technology headless - only as a powerful API, and detaching it from the user interface, where other technologies such as react and Next.js excel at.

Next.js is a popular react framework for building react applications which leverage server-side rendering. It has some features that make it well-suited for headless Drupal projects, including automatic code splitting, optimised performance, and server-side rendering.

In this tutorial we are going to first create a drupal app for creating and storing coffee drinks. We are then going to create a Next js app, fetch the available data from our drupal app and finally show a list of coffee drinks. We assume that you already have some experience with Drupal and Next.js

Drupal app

Install Drupal

We start by creating a new Drupal site. In our example we are going to create a web app that will show different coffee options. To create the drupal site we will use docksal, which is already setup in our machine. We just need to run:

$ fin project create

We are then prompted for app name:

1. Name your project (lowercase alphanumeric, underscore, and hyphen): 
drupal-headless-coffee-drinks

We are then prompted for app name:

Drupal 9 (Composer Version)

Now we continue with the installation and finally your app will be created via the CLI and the terminal will show you a url link to your newly created app

Open http://drupal-headless-coffee-drinks-app.docksal in your browser to verify the setup.
Look for admin login credentials in the output above.
DONE!  Completed all initialization steps.

Just above the message with the url to your app, we see credentials for the admin user of your drupal web app. Keep that information! Now we copy the credentials and navigate to the web app login

(drupal_app_url)/user/login 
# in our case: http://drupal-headless-coffee-drinks-app.docksal/user/login

Add Next.js module

Run the following command to install the Next.js module and dependencies.

$ composer require drupal/next

Enable API modules

  1. Visit /admin/modules
  2. Enable the following modules: Next.js and Next.js JSON:API.
enable api modules

Create coffee drink content type and add data

Now let’s create our coffee drinks data. First we need to create our coffee drink content type. To do that admin/structure/types and click on Add content type, as an example, fill in the following:

Name: Coffee Drink, then:

  • Click on Save and Manage Fields.
  • Click on Add field
  • Choose Text Plain
  • In the Label Field type “description”
add description and image fields

Then let’s add an Image field as well:

  • Click on add field
  • You can re-use and existing field (from article content typewhich is created by default)

Finally, let’s create some content in our database. To do that navigate to /admin/content and click on Add Content. Now we choose Coffee Drink and fill in the fields

add coffee drink content

I did the same for Americano, Espresso and Mocha and the final result is that I have 4 items created as “Coffee drink” type in our database. Final result is 4 content items - Coffee drinks stored in our CMS. Now let’s see how to fetch them in our Next.js, front-end app.

4 Coffee drinks content items created image

Next.js app

Let’s create our next app with the following parameters when prompted

$ npx create-next-app 
✔ What is your project named? … next-coffee-app 
✔ Would you like to use TypeScript with this project? … No / Yes 
✔ Would you like to use ESLint with this project? … No / Yes

Install Tailwind

Next, optionally, install tailwind to the project in order to style the application seamlessly later. To install tailwind did the following steps

  • npm install -D tailwindcss postcss autoprefixer
  • npx tailwindcss init -p
  • In tailwind.config.js add this:
/** @type {import('tailwindcss').Config} */ 
module.exports = { 
 content: [ 
   "./pages/**/*.{js,ts,jsx,tsx}", 
   "./components/**/*.{js,ts,jsx,tsx}", 
 ], 
 theme: { 
   extend: {}, 
 }, 
 plugins: [], 
} 
  • Go to global.css deleted everything and added the following directives to our CSS:
@tailwind base; 
@tailwind components; 
@tailwind utilities;

Create ENV

Create .env file and and added variables which point to our API url:

NEXT_PUBLIC_DRUPAL_BASE_URL=http://drupal-headless-coffee-drinks-app.docksal

Fetch coffee drinks data and pass it to component

// pages/index.tsx 
import Head from "next/head"; 
import { GetStaticPropsResult } from "next"; 
import { DrupalNode } from "next-drupal"; 
import { DrupalClient } from "next-drupal"; 
import CoffeeDrinkList from "../components/CoffeeDrinkList";

const drupal = new DrupalClient(process.env.NEXT_PUBLIC_DRUPAL_BASE_URL);

interface IndexPageProps { 
 nodes: DrupalNode[]; 
}

export default function IndexPage({ nodes }: IndexPageProps) { 
 return ( 
   <> 
     <Head> 
       <title>Next.js for Drupal</title> 
       <meta 
         name="description" 
         content="A Next.js site powered by a Drupal backend." 
       /> 
     </Head> 
     <CoffeeDrinkList drinks={nodes} /> 
   </> 
 ); 
}

export async function getStaticProps(): Promise< 
 GetStaticPropsResult<IndexPageProps> 
> { 
 const nodes = await drupal.getResourceCollection<DrupalNode[]>( 
   "node--coffee_drink", 
   { 
     params: { 
       "filter[status]": 1, 
       "fields[node--coffee_drink]": 
         "title,field_image,field_description,created", 
       include: "field_image,uid", 
       sort: "-created", 
     }, 
   } 
 );

IndexPage returns a JSX element representing a web page. The web page contains a Head component from the next/head module, which is responsible for setting the title and description of the page. The web page also contains a CoffeeDrinkList component, which is passed an array of drinks as a prop. The IndexPage function is passed an object containing an array of DrupalNode objects as its sole argument.

We also have a function - getStaticProps, which is used to generate the props for the IndexPage component. The getStaticProps function fetches a collection of nodes from a Drupal backend (in this case coffee drinks) using the DrupalClient class. The nodes are filtered by status and sorted by creation date, and the function returns an object containing the nodes as props for the IndexPage component.

Show data as list

// components/CoffeeDrinksList.tsx 
 import { DrupalNode } from "next-drupal";
 
 interface CoffeeDrinkListProps { 
   drinks: DrupalNode[]; 
 }
 
 const CoffeeDrinkList = ({ drinks }: CoffeeDrinkListProps) => { 
   return ( 
     <div className="h-full bg-white"> 
       <h1 className="mb-10 text-6xl font-black text-center"> 
         List of Coffee Drinks 
       </h1> 
       <div className="grid grid-cols-3 gap-4 items-center justify-items-center"> 
         {drinks?.length ? ( 
           drinks.map((drink) => ( 
             <div 
               key={drink.id} 
               className="max-w-sm rounded overflow-hidden shadow-lg m-2" 
             > 
               <img 
                 className="w-full h-[300px]" 
                 src={`${process.env.NEXT_PUBLIC_DRUPAL_BASE_URL}${drink.field_image.uri.url}`} 
               /> 
               <div className="px-6 py-4"> 
                 <div className="font-bold text-xl mb-2">{drink.title}</div> 
                 <p className="text-gray-700 text-base"> 
                   {drink.field_description} 
                 </p> 
               </div> 
               <div className="px-6 pt-4 pb-2"> 
                 <span className="inline-block bg-gray-200 rounded-full px-3 py-1 text-sm font-semibold text-gray-700 mr-2 mb-2"> 
                   Created: {drink.created} 
                 </span> 
               </div> 
             </div> 
           )) 
         ) : ( 
           <p className="py-4">No nodes found</p> 
         )} 
       </div> 
     </div> 
   ); 
 };
 
 export default CoffeeDrinkList;

CoffeeDrinkList is a component which takes an array of DrupalNode objects as a prop and returns a JSX element representing a list of coffee drinks. The component is passed an object containing an array of DrupalNode objects as its sole argument. The component maps over the array of DrupalNode objects, creating a new JSX element for each node. Each JSX element represents a single coffee drink and displays the title, description, and creation date of the drink, as well as an image of the drink.

If the drinks array is empty or null, the component displays a message indicating that no nodes were found.

The component also includes some CSS classes that are used to style the elements. These classes are from the tailwindcss library.

Final results

Now run the next app and see the final result

$ npm run dev

We ended up with a page showing all coffee drinks title, description, image and created date and time - data we fetched from our drupal app.

List of Coffee Drinks fetched from drupal api

Summary

In the article example we saw how to create a drupal app, add content types and data for that content type, expose our drupal api and consume it with a next js app to show a list of coffee drinks fetched from the database to our users. For more complex solutions, which might require authentication, adding, deleting and updating data, refer to the documentation of Next.js for Drupal: https://next-drupal.org/

SUBSCRIBE TO OUR NEWSLETTER

Share this article:

SUBSCRIBE TO OUR NEWSLETTER

Related Blog Articles

    Why startups hesitate to work with a bespoke software development agency – and how we address every concern

    Blog

    Why startups hesitate to work with a bespoke software development agency – and how we address every concern

    Startups often hesitate to work with software agencies due to concerns over cost, control, and flexibility. Discover how Bulcode’s bespoke software solutions address each challenge, ensuring growth and agility.

    Written by Svetoslava Angelova
    Nov 05, 20245 min read
    Building a high-performing Agile team: Our proven approach

    Blog

    Building a high-performing Agile team: Our proven approach

    Discover how we build high-performing Agile teams by defining clear roles, fostering collaboration, and using flexible tools.

    Written by Svetoslava Angelova
    Aug 27, 20248 min read
    Drupal 11: What to expect? Comprehensive guide to new features and enhancements

    Blog

    Drupal 11: What to expect? Comprehensive guide to new features and enhancements

    Drupal 11 is out! In this article, discover it's exciting features and improvements. Upgrade now to redefine your digital strategy with Bulcode's expert support.

    Written by Svetoslava Angelova
    Aug 05, 20247 min read
    Single Directory Components in Drupal core: A comprehensive overview

    Blog

    Single Directory Components in Drupal core: A comprehensive overview

    Explore how Single Directory Components (SDC) in Drupal Core streamline the development process by encapsulating component-related files into a single directory. Learn about the benefits of SDCs and follow a step-by-step guide to implement them in your Drupal projects.

    Written by Nikolay Tsekov
    Aug 07, 20244 min read
    Which IT engagement model is right for you?

    Blog

    Which IT engagement model is right for you?

    Fixed price, time and materials, or dedicated teams? Consider carefully all the pros and cons of the engagement model for your project.

    Written by Svetoslava Angelova
    Sep 26, 202210 min read
    Varna and Burgas airports' websites use React components in Drupal

    Blog

    Varna and Burgas airports' websites use React components in Drupal

    Drupal is a modular system whose functions can be adapted to many different requirements, which is particularly important for public administration projects.

    Written by Mihail Shahov
    Nov 04, 20224 min read
    Laravel Mix - a simple and powerful wrapper around Webpack

    Blog

    Laravel Mix - a simple and powerful wrapper around Webpack

    Laravel Mix provides a fluent API for defining webpack build steps for your Laravel application using several common CSS and JavaScript pre-processors.

    Written by Stefani Tashkova
    Nov 15, 20224 min read
    What is Scrum?

    Blog

    What is Scrum?

    Scrum is a part of the Agile methodology. It is the most popular framework for agile development, and it is a simple process framework.

    Written by Svetoslava Angelova
    Nov 20, 20224 min read
    Roles in Scrum

    Blog

    Roles in Scrum

    Scrum roles and how you can fold them into your organisation.

    Written by Svetoslava Angelova
    Nov 21, 20224 min read
    Scrum events

    Blog

    Scrum events

    Scrum defines several events (sometimes called ceremonies) that occur inside each sprint: sprint planning, daily scrum, sprint review, and sprint retrospective.

    Written by Svetoslava Angelova
    Nov 22, 20223 min read
    Scrum artefacts

    Blog

    Scrum artefacts

    In software development, the term “artefact” refers to information that stakeholders and the scrum team use to describe a product that’s being developed.

    Written by Svetoslava Angelova
    Nov 23, 20222 min read
    Hire dedicated software developers (teams)

    Blog

    Hire dedicated software developers (teams)

    Tired of raising expenses with your in-house development team? Why not get a dedicated team at 40% to 60% of the cost?

    Written by Mihail Shahov
    Jul 17, 20203 min read

    GET IN TOUCH

    Have a project you'd like to launch?