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

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

Connected mode – Sitecore required, hosts locally, content data from Sitecore.

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 TopMenuWithConnectedMode

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

anything as per your choice.

  • Define the template fields of TopMenuWithConnectedMode component. This needs to be defined at /sitecore/definitions/components/TopMenuWithConnectedMode.sitecore.js
  • Define the component at src/components/TopMenuWithConnectedMode/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';
import { loader as gqlLoader } from 'graphql.macro';
import GraphQLData from '../../lib/GraphQLData';

const ConnectedDemoQuery = gqlLoader('./query.graphql');

const TopMenuWithConnectedMode = (props) => {
  console.log('connected');
  console.log(props);
  const graphQLResult = props.connectedQuery;

  // Async loading and error handling
  // Remember to never return null from a JSS component when loading,
  // this will break Experience Editor.
  const { error, loading } = graphQLResult;

  // Query results load in using the name of their root field (see query.graphql)
  const { datasource, contextItem } = graphQLResult;

  return (
    <div data-e2e-id="graphql-connected">
      {loading && <p className="alert alert-info">GraphQL query is executing...</p>}

      {error && <p className="alert alert-danger">GraphQL query error: {error.toString()}</p>}
      {datasource && <div></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 GraphQLData(ConnectedDemoQuery, { name: 'connectedQuery' })(
  TopMenuWithConnectedMode
);
  • Write the GraphQl query. Create a file named query.graphql at path src/components/TopMenuWithConnectedMode

query.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 ConnectedDemoQuery _operation_. This makes it
# very efficient at gathering data from multiple sources.

query ConnectedDemoQuery($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 })
    }
  }
}
  • 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: TopMenuWithConnectedMode
        fields:
          heading: TopMenu
  • Deploy the items to your sitecore instance by running below command.

jss deploy items -c -d

While running the above command, I got below error

IMPORT WARNING(S) OCCURRED!
/sitecore/content/nav-graphql-connected-demo/home has ID {A07BB008-227A-540C-8E2C-3A502523F1EC}, which does not match the expected consistent ID {80530AF8-D8A3-5AF2-9035-049F7FA9ED7B}. If this item is imported to another Sitecore instance where it does not exist, it will get a different item ID.

This is because there is already a home item (Please note there are different approaches to develop a Sitecore JSS project, you can refer to https://jss.sitecore.com/)

Delete the home item and run the command again.

  • Publish the site
  • Run the application in connected mode. Use the below command

jss start:connected

I see lots of compilation errors

This is because I think major improvements to the way line lengths are handled/trimmed in this version

You can either fix them or get it ignored.

I added below tag in .eslintrc

“prettier/prettier”: [“error”, {      “endOfLine”:”auto”    }], 

Save it and run the application in connected mode once again.

Ohh nothing comes up.

I see the console log in browser and it clearly states placeholder issues.

  • Fix the root placeholders
    As I defined three placeholders in my layout.js file, so I need to register them with the root placeholders.

Add the root placeholders in package.json file.

Now before running the app, lets create some more items. I added few more items which will appear as pages in Sitecore.

Deploy the app’s sitecore contents by running below command

jss deploy items -c -d

This will publish your root placeholders and new items created to your sitecore instance.

You can publish your site if required.

Run the app now in connected mode.

You can apply the stylesheets as per your need.

Happy Sitecoreing!!