This repository has been archived and is no longer maintained.
This is a Next.js corporate starter site using Kontent by Kentico as a CMS.
Once you have access to the environment variables you'll need, deploy the example using Vercel:
Execute create-next-app
with npm or Yarn to bootstrap the example:
npx create-next-app --example https://github.com/Kentico/kontent-starter-corporate-next-js kontent-starter-corporate-next-js
# or
yarn create next-app --example https://github.com/Kentico/kontent-starter-corporate-next-js kontent-starter-corporate-next-js
-
Set up environment variables
-
Copy the
.env.template
file in this directory to.env
(which will be ignored by Git):cp .env.template .env
-
-
Run the development server
npm run dev # or yarn dev
🎉 Open http://localhost:3000 with your browser to see the result.
By default, the content is loaded from a shared Kontent project. If you want to use your own clone of the project so that you can customize it and experiment with Kontent, continue to the next section.
Variable | Required | Description |
---|---|---|
KONTENT_PROJECT_ID | YES | Project identification described in the Setup section |
KONTENT_PREVIEW_API_KEY | NO | Project key allowing to load non-published content described in the Setup and Preview sections |
PREVIEW_SECRET | NO | A key securing the preview content access via application described in Preview entering section |
STATIC_EXPORT | NO | If you choose to Static export of your site |
-
Create an account on Kontent
-
After signing up, create an empty project.
-
Go to the "Project Settings", select API keys and copy the following keys for further reference
- Project ID
- Management API key
-
Use the Kontent Backup Manager and import data to the newly created project from
kontent-backup.zip
file via command line:npm i -g @kentico/[email protected] # or yarn global add @kentico/[email protected] kbm --action=restore --projectId=<Project ID> --apiKey=<Management API key> --zipFilename=kontent-backup
-
Go to your Kontent project and publish the imported items.
- Set env variables on
.env
:KONTENT_PROJECT_ID
- Should be the Project ID inProject settings
>API keys
.KONTENT_PREVIEW_API_KEY
- Should be the Preview API key inProject settings
>API keys
. Set this value if you want to see non-published content. If you need a more complex setup, jump to the Preview section.
Run the development server:
npm run dev
# or
yarn dev
🎉 Open http://localhost:3000 with your browser to see the result.
You can start editing the page by modifying content in Kontent project. The page auto-updates as you edit the content. If you don't have KONTENT_PREVIEW_API_KEY
specified, you need to publish the changes in order to see the changes on site.
This section describes the application from the data modeling point of view as well as application architecture approach.
Content is modeled to be ready for being used in the Web Spotlight. Web spotlight is not turned on by default intentionally, because this starter should also serve to the users without the feature on.
The screen was taken from Relations tab. For a better idea, check out the content type relationships graph.
The structure of the model is defined by linked items element called "Subpages"
- Homepage - also stores configuration data like color palette, font specification, site logo, or social networks information
- Navigation Item - mainly for defining the sitemap structure with the slugs.
SEO information is modeled as a content type snippet called "SEO".
This snippet is placed as a part of structural types. Every sitemap entry (the content item based on structural types and a post
) allows specifying SEO metadata. This metadata is used in a custom App components (_app
) component to be rendered on the page.
Content for these structural wrappers is defined by linked items element called "Content" with limitation to exactly one item of the type:
- Simple page - This content type is mapped to the simple_page.js. It is a simple page, containing title, subtitle, image and content.
- Landing page - This content type is mapped to the landing_page.js. The more complex page consists of sections (see /components/sections).
- Listing page - This content type is mapped to the listing_page.js. This page allows to specify which content types should be listed under it. Currently, the only supported in Post content type.
These types are then using specific layouts for rendering.
When you turn on the Web Spotlight. New content types "Homepage" and "Page" will be generated. In order to accommodate the content types, it is required to:
- Remove the Page content type, because its responsibilities are handled by "Navigation Item" content type.
- Transfer content model structure from "old" homepage content type to newly created one. The only difference is the "Subpages" element that will be modeled by Subpages element type. It is important to keep the codenames of the element the same.
- Transfer the data from old "homepage".
- Remove the "old" homepage content item and the "old" content type and set the new homepage content item codename to "homepage".
These steps are easily scriptable by creating a migration using Kontent CLI. To see the progress of including it to this starter, follow this issue.
The application is using Next.js Catch all route to handle all of the routes. The logic is centralized in pages/[[...slug]].js
.
Described sitemap construction and configuration loading from Home Page structural type suits better for projects being built from scratch. In these situations, you need to have the sitemap and configuration (pallette, font, etc.) flexible and easily manageable from the headless CMS without necessity to change the code. This gives you flexibility to spin up the website and configure the project with No Code.
This approach requires to make 2 requests per route to reload
Mappings
andConfig
objects. This additional work takes a couple of seconds for the development environment to spin up. For the production build, the results are still fast enough.If you already know the configuration values and the sitemap structure, it is better to use Next.js dynamic routes showcased in Next.js Kontent blog repository, but the approach used for RichText links resolution, Preview URLs and Web Spotlight setup has to be adjusted to respect this new way of routing.
To define the sitemap and its mapping to specific content items there is getContentPaths
method in lib/api.js. Internally it uses getSitemapMappings
method which loads "homepage" item and its child items to the specific depth and then traverses down. This process creates a mapping between the sitemap (routes) and the content items storing data for the specific route.
One example item from the sitemap mappings:
{
params: {
slug: ["about", "more_information"],
navigationItem: {
id: "ABC",
codename: "lorem",
type: "navigation_item",
language: "default",
collection: "default",
// whole system information
},
contentItem: { // could be same i.e. for blog posts
id: "XYZ",
codename: "ipsum",
type: "simple_page",
language: "default",
collection: "default",
// whole system information
},
}
}
For every single route, Next.js is loading data using the getPageStaticPropsForPath
method. Internally, it reloads the site structure via getSitemapMappings
method and then identifies the content items to load the data. Then loads the data using Delivery client and passes them as the Next.js props
to the Next.js processing.
Static props returned from getPageStaticPropsForPath
:
{
// seo title, description, ....
seo,
// URL -> CONTENT ITEM mapping from above
mappings,
// RAW DATA FROM KONTENT
data: {
// data for layout (font, header logo, color palette, ...)
config,
// main content of the page
page,
// if the `page` is a `listing_page` type - linked items are passed in this item
listingItems,
// if any section of the page is a `listing_section` related items are stored there in the dictionary under the codename of the listing section content item
listingSections
},
};
The data passed as Next.js props
are then populated in _app.js
and other components in the component tree.
Currently, the sitemap is reloaded for every request. The following approach was selected because there is currently no way to pass more information than just a path from
getStaticPaths
togetStaticProps
. See the official Next.js GitHub discussion comment for more information. It is possible to extend the implementation with the caching, this approach is about to be application specific, so it is not part of the starter.
Application is using the codename of the content type to load proper react component and render it. If the application does not find the proper component, it fails or displays special UnknownComponent in case of development environment.
const componentName = upperFirst(camelCase(get(section, "system.type", null)));
const Component = sections[componentName];
if (process.env.NODE_ENV === "development" && !Component) {
console.error(
`Unknown section component for section content type: ${contentType}`
);
return (
<UnknownComponent key={section_idx} {...this.props}>
<pre>{JSON.stringify(section, undefined, 2)}</pre>
</UnknownComponent>
);
}
return <Component key={section_idx} {...props} section={section} />;
Reference:
Next.js offers embedded possibility to preview unpublished content - the preview mode. This feature is integrated with Kontent preview in this starter. Once the preview is enabled, all api calls are performed to the Kontent Preview endpoints.
There are two Next API routes - /api/preview
and /api/exit-preview
- that works as described in Next.js docs.
To enter the preview, just access /api/preview
API route with the preview secret you set in your environment variables.
http://localhost:3000/api/preview?secret=PREVIEW_SECRET
If you don't have your
PREVIEW_SECRET
environment variable set, your preview is not secured and could be accessed by anybody. Read more on Official Next.js docs.
Once your secret is verified, you will be redirected to home page and you could see non-published content and the toolbar that allows you to exit the preview.
Kontent offers a possibility to set the preview URLs for the content types. The starter (/api/preview
route) is already prepared to consume this preview URLs for structural types and for the Post
content type.
To allow that for your project, just set the Preview URLs for Navigation item
, Homepage
, and Post
content type to (fill values in angle brackets):
https://<YOUR-HOST-DOMAIN>/api/preview?secret=<PREVIEW_SECRET>&redirectItemCodename={Codename}
Once the preview is enabled a new toolbar pops up on the top of the page. This allows to close the preview (the "EXIT PREVIEW" button leads to the /api/exit-preview
API route).
Next.js provides multiple ways to fetch the data. This starter is mainly focused to be used in two of these modes. Incremental static regeneration and static export.
This approach serves well for most of the use cases. Application is generated as static HTML and then re-hydrating React components with JSON data objects. It supports Preview functionality. The publishing process with incremental static regeneration ensures that the new content is propagated in the background and once it is ready, the application starts to serve it instead of the stale content. Revalidation period is set to 5s as you can see on getStaticProps
method in [[...slug]].js
component.
This deployment requires a place to run Node.js code. Since the starter target configuration is set to serverless
(see next.config.js
file information), it is possible to use lambda functions to run this server code. This fits the Next.js recommended provider Vercel. But you could use i.e. Netlify in combination with the netlify-plugin-next-js, or any other cloud provider and configure lambdas the same way as Netlify plugin does. The second option is to switch the target to the server
and use a Node.js server deployed to any of your cloud providers.
To run this locally, run following commands:
yarn build
yarn start
It is possible to pre-generate all site and deploy it right to the CDN without the necessity to have a place to run Node.js code (lambda function/server). This removes the option of having the content up-to-date out-of-the-box when your content changes as well as preview functionality. You need to set up the webhooks and their handlers, that regenerate the site and re-deploy the generated content to your CDN for both released and preview content.
⚠ To be able to run the export, the site has to be built without fallback pages. The starter is turning the fallback pages on unless the STATIC_EXPORT
environment variable is set to true
.
To generate the site locally, run fun following command:
yarn static-export
It is also possible to use this starter for server-side rendering, it requires you to implement
getServerSideProps
in Next.js Page component, but as the Next.js documentation states - "You should usegetServerSideProps
only if you need to pre-render a page whose data must be fetched at request time" - and that is not the primary requirement for this starter.If you want to see the example checkout What’s the best place to host Next.js site? by Ondřej Polesný.
A Rich text element could carry more than just a text. It could contain links, images, components, and inline linked items. The starter offers a /style-guide
section to showcase the options to resolve complex structure into the React components.
The /style-guide
is a page based on the simple_page
layout containing title, sub, and the content in a form of a rich text element. This rich text element contains a showcase of various typographical examples (headlines, lists, tables) and also images, components, and links to other content items. To parse and resolve them, there is a pair of components. The first one is RichTextComponent
containing the parsing logic (using html-react-parser
library) and offering the possibility to implement the resolution. And the second one defining the resolution logic from rich text blocks to React Components - the RichText
component.
The usage is simple. To resolve the rich text element, you place the RichText
component and provide a rich text element object and then propagate all other props used to load appropriate data (linked items data, information about mapping to be able to resolve URL to specific content item).
<RichText
{...props} // Used to load mappings and linked items data
richTextElement={get(page, "content", null)}
/>
In /style-guide
, you can see these results.
Rich Text Component
Rich Text Image
Rich Text Component
💡 You could use a different approach to resolve the rich text element blocks. It is possible to use the embedded support in Javascript SDK that allows resolving blocks into the
string
objects and then utilize libraryreact-jsx-parser
to transform this string representation to React components This approach however requires the recreation of the model classes when you need them from the JSON object form because Next.js/React does not allow passing class objects viaprops
of a React Component. If you want to get more detailed information about this topic, feel free to raise the question issue.
The application is using Material Design. The main theme is configured in _app.js. Components are styled using makeStyles
method to ensure isolation.
import React from "react";
import get from "lodash.get";
import { Container, makeStyles } from "@material-ui/core";
/*styles
const useStyles = makeStyles((_theme) => ({
section: {
background: "red",
},
}));
*/
function FeaturesSection(props) {
const section = get(props, "section", null);
const classes = {};
return (
<section
id={get(section, "system.codename", null)}
className={classes.section}
>
<Container>Section: {get(section, "system.codename", null)}</Container>
</section>
);
}
export default FeaturesSection;
There are some additional steps done to allow Server rendering. The concept of the app was used from Official Next.js example for material design.
Follow this issue to see how to use configurable theme definition in Next.js app.
To learn more about Next.js, take a look at the following resources:
- Static site or server-rendered? Next.js enables both - the landing page describing Kontent and Next.js capabilities by Kontent
- Kontent + Next.js blog example - complex sample project setup including i.e. preview functionality, listing, Tailwind CSS, ...
- Kontent + Next.js boilerplate - Simple boilerplate showcases Next.js static generation feature using Kontent as the data source.
- Next.js Documentation - learn about Next.js features and API.
- Learn Next.js - an interactive Next.js tutorial.
- This Mission: Next.js from the Ground Up
- Using the Next image component with Kontent assets by Chris Meagher
- Using Azure Pipelines to build and deploy a Next.js app to Azure app services by Chris Meagher
- Build and Deploy a Next.js Blog with Kontent and Vercel by Ondřej Chrastina
- Dynamic routing with Kontent and NextJS by Unplatform
- Solving content preview with Next.js Preview Mode by Richard Shackleton
- How to use Highlight.js on a Next.js site by Ondřej Polesný
- Pre-rendered, server-rendered, or hybrid: Which should I use? by Tom Marshall