Blogs
Freddy Montes
Senior Frontend Developer

Get started building pages with dotCMS, Next.js and Tailwind CSS

Aug 02, 2021
By: Freddy Montes
dotCMS provides you with the perfect API to build all the pages you need for your JamStack site. In this post we are going to show you how to build your pages using Next.js and Tailwind CSS. The first step is to initialize a new Next.js project: ```bash yarn create next-app project-name ``` To make sure everything is OK, let's test the project: ```bash cd project-name yarn dev ``` Open [localhost:3000](http://localhost:3000/) and you should see: ![Nextjs and dotCMS](/img/blog/nextjs.png) Now you have an initialized Nextjs project. When you start the project in `dev` you have hot-code reloading, error reporting and more. ## dotCMS and Next.js dotCMS provides a series of API calls that you can consume with Next.js, and create pages based on that content. fFor this tutorial we'll be using Page API. ### dotCMS Page API For this tutorial we'll be using the [Page API](https://dotcms.com/docs/latest/page-rest-api-layout-as-a-service-laas), which gives us all the information of a page created with dotCMS this includes: 1. The page layout consisting of rows, columns, containers and content. 2. The content of those pages The top level object looks like this: ```json { canCreateTemplate: true, containers: {}, layout: {}, numberContents: 21, page: {}, site: {}, template: {}, viewAs: {}, } ``` To build a page we will focus on three particular properties ```json { containers: {}, layout: {}, page: {}, } ``` - `containers`: contains all the information about the content associated with the page and states which container the content belongs to. - `layout`: handles all the information about the page layout, rows, columns. Including their sizes, offsets and a reference to the content inside each column. - `page`: contains the page data such as title, description, SEO etc. ## Next.js Next.js is a framework based on React that allows us to create web applications quickly. It's main feature is that it allows you to create pages generated at build time or pages generated on each request (server side rendered). The routing system is file based and also has dynamic routes which makes it perfect for integrating with the dotCMS page system. ### The process of creating pages The process will be as follows: 1. The user enters in the browser: `https://yoursite.com/`. 2. Inside Next.js we fetch the main page `/` to dotCMS using the Page API. 3. Now in Next.js, with the Page API response we create the React components needed to display the page. ![dotCMS NextJs and Tailwind CSS](/img/blog/dotcms-nextjs.png) Since we want to use Tailwind for styling, we start by adding it to our project. ## Installing and configuring Tailwind CSS We install the dependencies: ```bash yarn add tailwindcss@latest postcss@latest autoprefixer@latest ``` Next, generate your `tailwind.config.js` and `postcss.config.js` files: ```bash npx tailwindcss init -p ``` This will create a `tailwind.config.js` file with your minimal configuration: ```jsx // tailwind.config.js module.exports = { purge: [], darkMode: false, // or 'media' or 'class' theme: { extend: {}, }, variants: { extend: {}, }, plugins: [], } ``` It will also create a `postcss.config.js` file that includes `tailwindcss` and `autoprefixer` already configured: ```jsx // postcss.config.js module.exports = { plugins: { tailwindcss: {}, autoprefixer: {}, }, } ``` At the moment this is all we need but if you want more information: 1. [Tailwind CSS configuration documentation](https://tailwindcss.com/docs/configuration) 2. [PostCSS preprocesador](https://tailwindcss.com/docs/using-with-preprocessors) ### Configure Tailwind to remove unused styles in production Tailwind CSS is a huge library and we have to make sure that we only send the code we are going to use to production. Open your `tailwind.config.js` and let's edit the purge `line` ```diff diff --git a/tailwind.config.js b/tailwind.config.js index 62dfdaf..d7f0874 100644 --- a/tailwind.config.js +++ b/tailwind.config.js @@ -1,5 +1,5 @@ module.exports = { - purge: [], + purge: ['./pages/**/*.{js,ts,jsx,tsx}', './components/**/*.{js,ts,jsx,tsx}'], darkMode: false, // or 'media' or 'class' theme: { extend: {}, ``` With this configuration we are telling Tailwind the folders of our pages and components so that it automatically removes all the unused styles when we build for production. ### Including Tailwind CSS Now we need to include Tailwind in our project since we have Tailwind configured correctly. We edit the `/styles/globals.css` file, remove everything and just leave these lines: ```css @tailwind base; @tailwind components; @tailwind utilities; ``` On the same page, we open the `pages/index.js` file and replace all its content with: ```jsx import Head from 'next/head' export default function Home() { return ( <> DotCMS - Next.js & Tailwind CSS

DotCMS - Next.js & Tailwind CSS

) } ``` With this we have a blank page with Tailwind ready to start development. ## Creating our page To create our first page it is useful to know how the routing system works in Next.js. Next.js has a file based routing system, all pages are created inside the `/pages` folder. If you create a `/pages/contact.jsx` file that automatically creates that route in your `https://yourdomain.com/contact` application. ### Server side render For this tutorial we are going to generate the page in the request. To do this we export the `getServerSideProps` function in the `/pages/index.js` page file. ```jsx export async function getServerSideProps(context) { return { props: {}, // will be passed to the page component as props } } ``` Inside this function we are going to make a request to dotCMS to obtain the object of the page. The result is going to be returned in a `props` object that Next.js will automatically pass to the `Home` component. ### Getting the page in dotCMS To make the page request we need an authentication token in dotCMS. You can get it: 1. [Through an API](https://dotcms.com/docs/latest/rest-api-authentication#ExampleJWT) 2. [Through the UI](https://dotcms.com/docs/latest/rest-api-authentication#APIToken) The url of the Page API is: `https://your.dotcms.com/api/v1/page/json/path/to/page`. ```jsx export async function getServerSideProps(context) { const { containers, layout, page } = await fetch("https://your.dotcms.com/api/v1/page/json/path/to/page", { headers: { Authorization: `Bearer ${AUTH_TOKEN}`, }, } ) .then((res) => res.json()) .then((data) => data.entity); return { props: { containers, layout, page }, // will be passed to the page component as props }; } ``` What we did in `getServerSideProps`. 1. A fetch request to the URL of the page in dotCMS. 2. Pass the token in the request header. 3. Extract `containers, layout, page` and return them in an object in the `props` property. This way we pass the page information to the `Home` component in order to render the content using components. ### Making the layout First let's understand what information is in the layout object of a dotCMS page. ```jsx { "layout": { "width": null, "title": "TITLE", "header": true, "footer": true, "body": { "rows": [ { "columns": [ { "containers": [ { "identifier": "IDENTIFIER", "uuid": "UUID" } ], "widthPercent": 100, "leftOffset": 1, "styleClass": "someclass", "width": 12, } ], } ] } } } ``` The grid is made of 12 columns. What we care about when building the page are the following properties: - `header` and `footer` booleans that indicates show/hide. - `body` includes the `rows` and `columns` information with an array ready to iterate and use components. - `columns` each element represents a `box`. - `containers` is an array of references to the containers containing the column. - the widthPercent` the width of the column in percent. - `leftOffset` the position within the grid where this column box starts. - `styleClass` class added by the content creator. - `width` the width of the column box. With this information we are going to create components with Tailwind classes. ### Row component We create `/components/Row.js` and add the following code: ```jsx export function Row({ children }) { return
{children}
} ``` This component is going to be used to render each row of the page layout, the grid containers of 12 columns, the vertical margin and the grid gap. Inside this component we are going to render a series of `` components, that we are going to create next. ### Column component We create `/components/Column.js` and add the following code: ```jsx export function Column({ children, leftOffset, width }) { const end = leftOffset + width; return
{children}
; } ``` In this case assign where each box of the layout starts and ends in the component. Tailwind provides the `col-start-N` and `col-end-N` classes and we have that information in the dotCMS layout object information. ### Using `` and ``. To obtain the layout from the `props` of the `Home` component we only need the following components: ```jsx export default function Home({ layout }) { const { rows } = layout.body; return ( <> DotCMS - Next.js & Tailwind CSS

DotCMS - Next.js & Tailwind CSS

{rows.map(({ columns }, i) => ( {columns.map(({ leftOffset, width }, k) => ( Column {k} ))} ))}
); } ``` Depending on the page you are on you should see something like this: ![Tailwind and dotCMS](/img/blog/tailwind-preview.png) ### Adding the content Now that we have the layout, we have to add the content of the page. The content lives in the containers and in the layout object inside `columns` we have a reference to these containers. The containers and the content they contain is in the `containers` property of the page. This object contains detailed information about all the content and containers. For the purposes of this post we are going to focus on the `contentlets` property, which is the object for each piece of content that is created and added to the page: ```json "containers": { "ID_OF_CONTAINER": { "contentlets": { "uuid-N": [ { "contentType": "ContentTypeName", "field1": "Field 1 Content", "fieldN": "Field N Content" } ] } } } ``` The most important property for creating components is `contentType` because based on this property we will be able to create a different React components for each Content Type. For example if you have a Content Type of type "Event" you are going to create a React Component to render "Event". The rest of the properties are going to be the `props` of the component. ### The `` component. For the mapping between the Content Type and it's components we create the component `components/Contentlet.js`. ```jsx import { Activity } from './content-types/Activity'; import { Product } from './content-types/Product'; const Components = { Activity, Product } function Fallback({ title }) { return

{title}

} export function Contentlet(contentlet) { const Component = Components[contentlet.contentType] || Fallback; return } ``` This component is going to be an intermediary to return the associated component content type of the contentlet we pass in. If the contentlet does does not exist it will return the `Fallback` component: 1. Create a component map `Components`. 2. Make a component to use as default in case we do not have the `Fallback` component created. 3. In the `Contentlet` component we look for the component in the map using the `contentType` property. This is where we have to match the Content Type in dotCMS and the component in Next.js and if we don't have a matching Content Type, we return the `Fallback` component. 4. Finally the "found" component or the Fallback is returned and the `contentlet` object is passed in as props. ### Using ``. Inside the `` component we iterate and render the `containers` and then iterate again to find the contentles inside the containers. ```jsx export default function Home({ layout, containers }) { const { rows } = layout.body; const containersData = containers; return ( <> DotCMS - Next.js & Tailwind CSS

DotCMS - Next.js & Tailwind CSS

{rows.map(({ columns }, i) => ( {columns.map(({ leftOffset, width, containers }, k) => ( {containers.map(({ identifier, uuid }, l) => { const contentlets = containersData[identifier].contentlets[`uuid-${uuid}`]; return (
{contentlets.map((contentlet, m) => { return ; })}
); })}
))}
))}
); ``` To locate the contentlets we use the `identifier` and the `uuid` returning an `array` of contentlets that are rendered with the `Contentlet` component. ## Conclusion 1. Generate the page with Next.js 2. Query the dotCMS APIs to get information from the page. 3. With the page object you can use Tailwind grids to create the layout. 4. Make a React component map for each type of content on the page. The combination of Next.js with the [powerful APIs of dotCMS](https://dotcms.com/docs/latest/web-apis) allows you to create your pages quickly and if you supplement this with the grids system and CSS utilities of Tailwind, you have a powerful combination that allows you to launch your web project to the next level.

Filed Under:

webinar

Real-time Editing of Your Jamstack App

Allow marketers and content creators to edit and manage your Jamstack application in context with dotCMS.

Watch Now

Recommended Reading

Why Global Brands Need a Multi-tenant CMS

Maintaining or achieving a global presence requires effective use of resources, time and money. Single-tenant CMS solutions were once the go-to choices for enterprises to reach out to different market...

14 Benefits of Cloud Computing and Terminology Glossary to Get You Started

What is cloud computing, and what benefits does the cloud bring to brands who are entering into the IoT era?

Headless CMS vs Hybrid CMS: How dotCMS Goes Beyond Headless

What’s the difference between a headless CMS and a hybrid CMS, and which one is best suited for an enterprise?