As the world of web development continues to evolve, dynamic sites have become increasingly popular for their ability to provide a more engaging and interactive user experience. There are numerous cases where we need to consume data from external sources (e.g., third party api) to display in the Sitecore Next.js based application where pages are not present in the Sitecore Content Tree.
In this tutorial, I will be focusing on building of Non-Sitecore Page and it’s Page Layout with Sitecore JSS and NextJS, and will be passing existing Sitecore NextJS components from the Non-Sitecore page to the Page Layout to render.
In this tutorial, I will be focusing on building of Non-Sitecore Page and it’s Page Layout with Sitecore JSS and NextJS, and will be passing existing Sitecore NextJS components from the Non-Sitecore page to the Page Layout to render.
Sitecore JSS is a headless CMS that provides developers with a way to build dynamic sites using React components. NextJS, on the other hand, is a React framework that provides server-side rendering and other performance optimizations.
By combining Sitecore JSS and NextJS, developers can build dynamic sites that are optimized for performance and user experience.
In this tutorial, I will be providing a step-by-step guide on how to build a NextJS page and it's layout, and reuse of existing components on Non-Sitecore Pages (NextJS page).
By combining Sitecore JSS and NextJS, developers can build dynamic sites that are optimized for performance and user experience.
In this tutorial, I will be providing a step-by-step guide on how to build a NextJS page and it's layout, and reuse of existing components on Non-Sitecore Pages (NextJS page).
In Next.js, a page is a React component that is used to define the content and behavior of a specific route in your application. Next.js have pages in the form of physical files which are stored in the /pages directory.
The Sitecore JSS uses the Next.js routing system and all route related to Sitecore JSS application defined in the file /src/pages/[[...path]].tsx and this route helps Sitecore JSS framework to render Pages which are created in Sitecore.
Here are some key things to know about Sitecore JSS pages:
The Sitecore JSS uses the Next.js routing system and all route related to Sitecore JSS application defined in the file /src/pages/[[...path]].tsx and this route helps Sitecore JSS framework to render Pages which are created in Sitecore.
Here are some key things to know about Sitecore JSS pages:
- Pages are defined in JSON format: In Sitecore JSS, pages are defined as JSON objects that contain the data and metadata for a specific route in your application. The JSON object can contain fields, components, placeholders, and other data that define the content and presentation of the page
- Sitecore JSS pages are created in the Sitecore content tree using a special page template. The page template contains fields and components that correspond to the JSON object structure for the page. When a JSS app requests a page, Sitecore retrieves the JSON object for the page and sends it back to the app
- Pages can be created using a variety of data sources: Sitecore JSS pages can be created using data from a variety of sources, including Sitecore content, external APIs, and custom data sources. This allows developers to create dynamic and personalized pages that incorporate data from multiple sources
- Pages can be enhanced with custom components: Sitecore JSS pages can be enhanced with custom components created using the JavaScript framework of your choice. These components can be shared across multiple pages to provide a consistent look and feel to your application
- Pages can be previewed and published using Sitecore: Sitecore JSS pages can be previewed and published using the Sitecore Experience Editor and Content Editor. This allows content authors to create and manage JSS pages alongside traditional Sitecore content
Next.js is a popular React-based framework for building server-side rendered (SSR) web applications. Here are some important things to know about Next.js pages:
- By default all Pages in a Next.js application are located in the /pages directory. Each page should have its own file with a unique name that matches the name of the route it represents. For example, the page for the /about route should be named about.js or about.tsx
- Pages are automatically routed: Next.js automatically routes URLs to the appropriate page based on the file name in the /pages directory. For example, a URL of /about will automatically route to the about.js or about.tsx file in the /pages directory
- Pages can be dynamic: Next.js allows you to create dynamic routes by using square brackets [ ] in the page file name. For example, a page file named posts/[foo].js would match any URL that starts with /posts/ followed by a variable foo. You can then access the value of the foo as a parameter in the getStaticProps() or getServerSideProps() function
- Pages can have different export functions: In addition to the default export of the page component, Next.js allows you to export other functions such as getStaticProps(), getServerSideProps(), and getStaticPaths(). These functions can be used to fetch data or specify dynamic routes for the page
- Pages can be enhanced with additional components: Next.js pages can be enhanced with additional React components such as layout components, navigation components, and footer components. These components can be shared across multiple pages to provide a consistent look and feel to your application
Sitecore JSS is a headless CMS that provides developers with a way to build dynamic sites using React components.
Sitecore JSS provides several benefits, including the ability to build dynamic sites quickly and easily, the ability to reuse components across multiple sites, and the ability to integrate with other Sitecore features and services.
Sitecore JSS uses the NextJS file-based routing system, so we can build pages which are not coming from Sitecore in the Sitecore NextJS application code base by creating the required page inside the Pages directory. When a file is added to the pages directory, it's automatically available as a route.
To create a Non-Sitecore JSS page and it’s Page Layout, you would typically follow these steps:
Sitecore JSS provides several benefits, including the ability to build dynamic sites quickly and easily, the ability to reuse components across multiple sites, and the ability to integrate with other Sitecore features and services.
Sitecore JSS uses the NextJS file-based routing system, so we can build pages which are not coming from Sitecore in the Sitecore NextJS application code base by creating the required page inside the Pages directory. When a file is added to the pages directory, it's automatically available as a route.
To create a Non-Sitecore JSS page and it’s Page Layout, you would typically follow these steps:
- Go to the pages directory in the Sitecore NextJS project code at Website\src\rendering\src\ and create the integrations folder: Here I am trying to create page hierarchy so created the integrations folder otherwise you can directly create page inside the pages directory/folder directly, e.g., the page for the /about route can be created as about.js or about.tsx.
-
Create the Website\src\rendering\src\pages\integrations\index.tsx file which will handle the route /integrations
The integrations page is NextJS Page so it will not be using Layout which are defined at Website\src\rendering\src\Layout.tsx for Sitecore pages.
├── src ├── pages ├── integrations ├── index.tsx
- In Next.js, a page layout is typically defined as a React component that wraps the actual page component. The layout component can then include common elements such as a header, footer, navigation, or any other component that you want to include on every page. Create the folder NonSitecorePageLayout at Website\src\rendering\src\layouts\ to store the Layout for Non-Sitecore Page
-
Create the index.ts and NonSitecorePageLayout.tsx file inside the folder Website\src\rendering\src\layouts\NonSitecorePageLayout\, and these file will be used to create the skeleton of NextJS Layout page:
The details of Website\src\rendering\src\layouts\NonSitecorePageLayout\index.ts file
export { NonSitecorePageLayout as default } from "./NonSitecorePageLayout"
This page is using NonSitecorePageLayout.tsx which contains the structure of Layout page. With this way we can define other layout components and include in the layout page. -
The details of NonSitecorePageLayout.tsx file
In this file we have defined the page structure of the page including the use of common components like header and footer so that it will be available to all pages who will be using this layout:
import Footer, { FooterProps } from "components/Navigation/Footer" import MainNavigation, { MainNavigationProps } from "components/Navigation/MainNavigation" import Head from "next/head" import React, { FC } from "react" type PageLayoutProps = { title?: string } export const NonSitecorePageLayout: FC<PageLayoutProps> = (props): JSX.Element => { const footerProps = { fields: { data: { item: { footerLogo: {}, }, links: { displayName: 'Footer', children: { results: [ { displayName: 'Follow Us', children: { results: [ { displayName: 'Facebook', icon: { value: 'faFacebookF' }, title: { value: '' }, field: { jsonValue: { value: { href: '' } }, }, }, { displayName: 'Youtube', icon: { value: 'faYoutube' }, title: { value: '' }, field: { jsonValue: { value: { href: '' } }, }, }, { displayName: 'Twitter', icon: { value: 'faTwitter' }, title: { value: '' }, field: { jsonValue: { value: { href: '' } }, }, }, { displayName: 'Instagram', icon: { value: 'faInstagram' }, title: { value: '' }, field: { jsonValue: { value: { href: '' } }, }, }, { displayName: 'Linkedin', icon: { value: 'faLinkedin' }, title: { value: '' }, field: { jsonValue: { value: { href: '' } }, }, }, ], }, }, { displayName: 'PLAY! Summit', children: { results: [ { displayName: 'Home', icon: { value: '' }, title: { value: '' }, field: { jsonValue: { value: { href: '/en', text: '', anchor: '', linktype: 'internal', class: '', title: '', target: '', querystring: '', id: '{68DC89A4-1B04-59A8-9C4E-3B49D6C61052}', }, }, }, }, { displayName: 'Sessions', icon: { value: '' }, title: { value: '' }, field: { jsonValue: { value: { href: '/en/sessions', text: '', anchor: '', linktype: 'internal', class: '', title: '', target: '', querystring: '', id: '{68DC89A4-1B04-59A8-9C4E-3B49D6C61052}', }, }, }, }, { displayName: 'Speakers', icon: { value: '' }, title: { value: '' }, field: { jsonValue: { value: { href: '/en/speakers', text: '', anchor: '', linktype: 'internal', class: '', title: '', target: '', querystring: '', id: '{1F4B781B-F2A5-5647-99DF-C0C369162C4D}', }, }, }, }, { displayName: 'Vendors', icon: { value: '' }, title: { value: '' }, field: { jsonValue: { value: { href: '/en/vendors', text: '', anchor: '', linktype: 'internal', class: '', title: '', target: '', querystring: '', id: '{774E44E8-0F30-5879-B847-AD233FFB41AA}', }, }, }, }, { displayName: 'Sponsors', icon: { value: '' }, title: { value: '' }, field: { jsonValue: { value: { href: '/en/sponsors', text: '', anchor: '', linktype: 'internal', class: '', title: '', target: '', querystring: '', id: '{66C99E47-7BBF-52D1-B1D7-4662B850744A}', }, }, }, }, { displayName: 'About Us', icon: { value: '' }, title: { value: '' }, field: { jsonValue: { value: { href: '/en/about-us', text: '', anchor: '', linktype: 'internal', class: '', title: '', target: '', querystring: '', id: '{2717574C-48A0-5469-85A8-A332DF71F1E4}', }, }, }, }, { displayName: 'News', icon: { value: '' }, title: { value: '' }, field: { jsonValue: { value: { href: '/en/news', text: '', anchor: '', linktype: 'internal', class: '', title: '', target: '', querystring: '', id: '{0E4A63DA-7496-557E-BF80-5BD52255E431}', }, }, }, }, ], }, }, { displayName: 'Join Us', children: { results: [ { displayName: 'Book Tickets', icon: { value: '' }, title: { value: '' }, field: { jsonValue: { value: { href: '' } }, }, }, { displayName: 'Become a Sponsor', icon: { value: '' }, title: { value: '' }, field: { jsonValue: { value: { href: '' } }, }, }, { displayName: 'Become a Vendor', icon: { value: '' }, title: { value: '' }, field: { jsonValue: { value: { href: '' } }, }, }, ], }, }, { displayName: 'Get Support', children: { results: [ { displayName: 'FAQ', icon: { value: '' }, title: { value: '' }, field: { jsonValue: { value: { href: '' } }, }, }, { displayName: 'Tech Support', icon: { value: '' }, title: { value: '' }, field: { jsonValue: { value: { href: '' } }, }, }, ], }, }, ], }, }, }, }, } as unknown as FooterProps; const mainNavProps = { fields: { data: { item: { id: '4790380A3F0C443AA954269ECB2D5566', path: '/sitecore/content/EdgeWebsite/Data/Config', headerLogo: { jsonValue: { value: { mediaid:'', thumbnailsrc: 'https://playsummit.sitecoresandbox.cloud/api/gateway/30940/thumbnail', src: 'https://playsummit.sitecoresandbox.cloud/api/public/content/83a458a1cb54401cab2308488bbd1031?v=bdb6447b&t=web', alt: 'PLAY! Summit logo - stacked light grey', height: '113', width: '274' } }, alt: 'PLAY! Summit logo - stacked light grey' }, }, links: { displayName: 'Main Navigation', children: { results: [ { displayName: 'Sessions', field: { jsonValue: { value: { href: '/sessions', class: '', id: '{68DC89A4-1B04-59A8-9C4E-3B49D6C61052}', querystring: '', anchor: '', target: '', title: '', linktype: 'internal', text: '' } } } }, { displayName: 'Speakers', field: { jsonValue: { value: { href: '/speakers', class: '', id: '{1F4B781B-F2A5-5647-99DF-C0C369162C4D}', querystring: '', anchor: '', target: '', title: '', linktype: 'internal', text: '' } } } }, { displayName: 'Vendors', field: { jsonValue: { value: { href: '/vendors', class: '', id: '{774E44E8-0F30-5879-B847-AD233FFB41AA}', querystring: '', anchor: '', target: '', title: '', linktype: 'internal', text: '' } } } }, { displayName: 'Sponsors', field: { jsonValue: { value: { href: '/sponsors', class: '', id: '{66C99E47-7BBF-52D1-B1D7-4662B850744A}', querystring: '', anchor: '', target: '', title: '', linktype: 'internal', text: '' } } } }, { displayName: 'About Us', field: { jsonValue: { value: { href: '/about-us', class: '', id: '{2717574C-48A0-5469-85A8-A332DF71F1E4}', querystring: '', anchor: '', target: '', title: '', linktype: 'internal', text: '' } } } }, { displayName: 'Integrations', field: { jsonValue: { value: { href: '/integrations', class: '', id: '', querystring: '', anchor: '', target: '', title: '', linktype: 'external', text: '' } } } } ] } }, }, }, } as unknown as MainNavigationProps; return ( <> <Head> <link rel="icon" href="/favicon.ico" /> <title>{props.title || "Play! Summit"}</title> </Head> <header> <MainNavigation {...mainNavProps}/> </header> {props.children} <footer> <Footer {...footerProps} /> </footer> </> ) }
-
Now go back to the integration page (Website\src\rendering\src\pages\integrations\index.tsx) created in step-2 and use the newly created layout
Here we are using Next.js, getLayout function that allows you to dynamically set the layout for a specific page or group of pages. This allows you to define the layout on a per-page basis, so its also called as Per-Page Layouts.
import Section, { SectionProps } from 'components/PageContent/Section'; import NonSitecorePageLayout from 'src/layouts/NonSitecorePageLayout'; import sectionJsonContent from '../../staticData/sectionComponents.Data.Integrations.Page.json'; const Integrations = (): JSX.Element => { return ( <> </> ); }; Integrations.getLayout = function getLayout() { const sectionData = { fields:sectionJsonContent } as unknown as SectionProps; return ( <NonSitecorePageLayout title="Integrations" > <Section {...sectionData}/> </NonSitecorePageLayout> ); }; export default Integrations;
The getLayout function is typically defined in a layout component and returns the layout component with any additional props that you want to pass down to the children components. Here's an example:In this page, we are using layout component and passing the layout properties and component to layout component so that it will render inside the layout The static data for Section component defined at Website\src\rendering\src\staticData\sectionComponents.Data.Integrations.Page.json// components/Layout.js const Layout = ({ children }) => { return ( <div> <Header /> <main>{children}</main> <Footer /> </div> ) } const getLayout = page => { return ( <Layout> {page} </Layout> ) } export default Layout export { getLayout }
{ "brightness": { "value": "light" }, "callToActionLink": { "value": { "href": "" } }, "content": { "value": "<p class=\"font-bold\">\r\nThis page will show the listing of different types of integrations on non-Sitecore pages\r\n</p>" }, "cssClass": { "value": "left-full" }, "title": { "value": "This page is non-Sitecore Page" } }
- The output of the Non-Sitecore page /integrations ( Website\src\rendering\src\pages\integrations\index.tsx) https://www.edge.localhost/integrations
By following the above listed steps, you can create the pages for your Sitecore NextJS application which are not part of your Sitecore Content Tree.
The Sitecore JSS and NextJS framework provides flexibility to extend your Layout and Pages as per your need to build a dynamic Sitecore JSS website which can provide numerous benefits, including improved performance, scalability, and user experience.
If you’re interested in learning more about Sitecore JSS and NextJS, be sure to check out the resources listed above and this blog site.
The Sitecore JSS and NextJS framework provides flexibility to extend your Layout and Pages as per your need to build a dynamic Sitecore JSS website which can provide numerous benefits, including improved performance, scalability, and user experience.
If you’re interested in learning more about Sitecore JSS and NextJS, be sure to check out the resources listed above and this blog site.
The code base present at the GitHub branch