How to build a simple navigation with Sitecore JSS using GraphQL integrated

Would you like to quick start with creating a simple navigation with Sitecore JSS using GraphQL integrated, then please follow below.

Integrated mode – Sitecore required, hosts on Sitecore, content data from Sitecore, server-side rendering.

For more details about Sitecore JSS application modes, please go through the link https://jss.sitecore.com/docs/fundamentals/application-modes

Now let’s start.

jss scaffold TopMenuWithIntegratedMode

where TopMenuWithIntegratedMode is the name of the component. You can name

anything as per your choice.

  • Define the template fields of TopMenuWithIntegratedMode component. This needs to be defined at /sitecore/definitions/components/TopMenuWithIntegratedMode.sitecore.js

 


// eslint-disable-next-line no-unused-vars
import { CommonFieldTypes, SitecoreIcon, Manifest } from '@sitecore-jss/sitecore-jss-manifest';
import fs from 'fs';

const query = fs.readFileSync(
  'sitecore/definitions/components/TopMenuWithIntegratedDemo.sitecore.graphql',
  'utf8'
);

/**
 * Adds the TopMenuWithIntegratedMode component to the disconnected manifest.
 * This function is invoked by convention (*.sitecore.js) when 'jss manifest' is run.
 * @param {Manifest} manifest Manifest instance to add components to
 */
export default function(manifest) {
  manifest.addComponent({
    name: 'TopMenuWithIntegratedMode',
    icon: SitecoreIcon.DocumentTag,
    graphQLQuery: query,
    fields: [
      { name: 'heading', type: CommonFieldTypes.SingleLineText },
    ],
    /*
    If the component implementation uses <Placeholder> or withPlaceholder to expose a placeholder,
    register it here, or components added to that placeholder will not be returned by Sitecore:
    placeholders: ['exposed-placeholder-name']
    */
  });
}

  • Write the GraphQl query. Create a file named TopMenuWithIntegratedDemo.sitecore.graphql at path /sitecore/definitions/components

    TopMenuWithIntegratedDemo.sitecore.graphql
# This file contains a GraphQL query that will be executed and the result provided to
# your JSS component. You can run this query in GraphiQL ($endpoint/ui) for a nice editing experience.

# Note that we're executing _two queries_ (datasource and contextItem)
# within the context of the IntegratedDemoQuery _operation_. This makes it
# very efficient at gathering data from multiple sources.

query IntegratedDemoQuery($datasource: String!, $contextItem: String!) {
  # Datasource query
  # $datasource will always be set to the ID of the rendering's datasource item
  # (as long as the GraphQLData helper is used)
  datasource(value: $datasource) {
    id
    name    
    }  

  # Context/route item query
  # $contextItem will always be set to the ID of the current context item (the route item)
  # (as long as the GraphQLData helper is used)
  contextItem: item(path: $contextItem) {
    id
    # Get the page title from the app route template
    ...on AppRoute {
      pageTitle {
        value
      }
    }

    # List the children of the current route
    children(requirePresentation: true) {
      id
      # typing fragments can be used anywhere!
      # so in this case, we're grabbing the 'pageTitle'
      # field on all child route items.
      ...on AppRoute {
        pageTitle {
          value
          jss
        }
      }
      url(options: { disableLanguageEmbedding: true })
    }
  }
}
  • Define the component at src/components/TopMenuWithIntegratedMode/index.js

index.js

import React from 'react';
import { Text, Link } from '@sitecore-jss/sitecore-jss-react';
import { Link as RouterLink } from 'react-router-dom';

const TopMenuWithIntegratedMode = (props) => {
  console.log(props);
  // Query results in integrated GraphQL replace the normal `fields` data
  // i.e. with { data, }
  const { datasource, contextItem } = props.fields.data;

  return (
    <div data-e2e-id="graphql-integrated">
      {datasource && (
        <div>
          <br />
        </div>
      )}
      {contextItem && (
        <div>
          <nav>
            <div className="top_menu">
              <div className="container_12">
                <div className="grid_12">
                  <div id="mydroplinemenu" className="droplinebar">
                    <ul>
                      {contextItem.children.map((child) => (
                        <li key={child.id}>
                          <RouterLink to={child.url}>{child.pageTitle.value}</RouterLink>
                        </li>
                      ))}
                    </ul>
                  </div>
                </div>
              </div>
            </div>
          </nav>
        </div>
      )}
    </div>
  );
};

export default TopMenuWithIntegratedMode;

 

  • Update the default layout at path /src/layout.js

layout.js

import React from 'react';
import { Placeholder, VisitorIdentification } from '@sitecore-jss/sitecore-jss-react';
import { NavLink } from 'react-router-dom';
import { withTranslation } from 'react-i18next';
import Helmet from 'react-helmet';

// Using bootstrap is completely optional. It's used here to provide a clean layout for samples,
// without needing extra CSS in the sample app. Remove it in package.json as well if it's removed here.
import 'bootstrap/dist/css/bootstrap.css';
import './assets/app.css';
import logo from './assets/sc_logo.svg';

/*
  APP LAYOUT
  This is where the app's HTML structure and root placeholders should be defined.

  All routes share this root layout by default (this could be customized in RouteHandler),
  but components added to inner placeholders are route-specific.
*/

const Layout = ({ route }) => (
  <React.Fragment>
    {/* react-helmet enables setting <head> contents, like title and OG meta tags */}
    <Helmet>
      <title>
        {(route.fields && route.fields.pageTitle && route.fields.pageTitle.value) || 'Page'}
      </title>
    </Helmet>

    {/*
      VisitorIdentification is necessary for Sitecore Analytics to determine if the visitor is a robot.
      If Sitecore XP (with xConnect/xDB) is used, this is required or else analytics will not be collected for the JSS app.
      For XM (CMS-only) apps, this should be removed.

      VI detection only runs once for a given analytics ID, so this is not a recurring operation once cookies are established.
    */}
    <VisitorIdentification />

    {/* root placeholder for the app, which we add components to using route data */}
    <div className="container-fluid">
      <div className="row">
        <Placeholder name="main-header" rendering={route} />
      </div>
      <div className="row">
        <Placeholder name="main-body" rendering={route} />
      </div>
      <div className="row">
        <Placeholder name="main-footer" rendering={route} />
      </div>
    </div>
  </React.Fragment>
);

export default Layout;

 

  • Create the route for home page under /data/routes
# This is a route layout definition.
# The route definition defines which Sitecore components are present on a route,
# what their content data is, and which _placeholder_ they are placed in.

# This particular route definition is for the home route - '/', so it defines the
# components shown on the initial page of the app.

id: home

# Route-level fields are appropriate for page level data like <title> contents
# Define route level fields in /sitecore/definitions/routes.sitecore
fields:
  pageTitle: Welcome to Sitecore JSS
# Define the page layout starting at the root placeholder - in this case, ''
# root placeholder names are defined in the package.json config section (required for Sitecore deployment)
placeholders:
  main-header:
      - componentName: TopMenuWithIntegratedMode
        fields:
          heading: TopMenu
  • Deploy the app to your sitecore instance by running below command.

jss deploy app -c -d

  • Publish the site

Now try run the website http://nav-graphql-integrated-demo.dev.local/

I used hostname as nav-graphql-integrated-demo.dev.local

Cannot read property ‘datasource’ of undefined
TypeError: Cannot read property ‘datasource’ of undefined
at TopMenuWithIntegratedMode (C:\inetpub\wwwroot\sc101sc.dev.local\dist\nav-graphql-integrated-demo\server.bundle.js:29670:39)
at d (C:\inetpub\wwwroot\sc101sc.dev.local\dist\nav-graphql-integrated-demo\server.bundle.js:15299:498)
at bb (C:\inetpub\wwwroot\sc101sc.dev.local\dist\nav-graphql-integrated-demo\server.bundle.js:15302:16)
at a.b.render (C:\inetpub\wwwroot\sc101sc.dev.local\dist\nav-graphql-integrated-demo\server.bundle.js:15308:43)
at a.b.read (C:\inetpub\wwwroot\sc101sc.dev.local\dist\nav-graphql-integrated-demo\server.bundle.js:15307:83)
at exports.renderToString (C:\inetpub\wwwroot\sc101sc.dev.local\dist\nav-graphql-integrated-demo\server.bundle.js:15318:138)
at process (C:\inetpub\wwwroot\sc101sc.dev.local\dist\nav-graphql-integrated-demo\server.bundle.js:22423:20)
at processTicksAndRejections (internal/process/task_queues.js:93:5)
Description: An unhandled exception occurred.

Exception Details: Sitecore.JavaScriptServices.ViewEngine.NodeServices.HostingModels.NodeInvocationException: Cannot read property ‘datasource’ of undefined
TypeError: Cannot read property ‘datasource’ of undefined
at TopMenuWithIntegratedMode (C:\inetpub\wwwroot\sc101sc.dev.local\dist\nav-graphql-integrated-demo\server.bundle.js:29670:39)
at d (C:\inetpub\wwwroot\sc101sc.dev.local\dist\nav-graphql-integrated-demo\server.bundle.js:15299:498)
at bb (C:\inetpub\wwwroot\sc101sc.dev.local\dist\nav-graphql-integrated-demo\server.bundle.js:15302:16)
at a.b.render (C:\inetpub\wwwroot\sc101sc.dev.local\dist\nav-graphql-integrated-demo\server.bundle.js:15308:43)
at a.b.read (C:\inetpub\wwwroot\sc101sc.dev.local\dist\nav-graphql-integrated-demo\server.bundle.js:15307:83)
at exports.renderToString (C:\inetpub\wwwroot\sc101sc.dev.local\dist\nav-graphql-integrated-demo\server.bundle.js:15318:138)
at process (C:\inetpub\wwwroot\sc101sc.dev.local\dist\nav-graphql-integrated-demo\server.bundle.js:22423:20)
at processTicksAndRejections (internal/process/task_queues.js:93:5)

If you see above error most likely because the GraphQL query is not updated on Sitecore or it is not published.

Make sure GraphQL query is passed in template definition

Publish the site (if needed)

Now try run the website http://nav-graphql-integrated-demo.dev.local/

You can apply the stylesheets as per your need.

Happy Sitecoreing!!