This post is part of a series - Sitecore JavaScript Services (JSS) Sandbox

My "jss-sandbox" site is going to showcase upcoming Sitecore community events. But before I could start the fun part of building components, I needed to create some mock content.

Adding a new route definition

First, I created a new route type using manifest.addRouteType . This defined route-level fields specific to event pages. Note that there’s no field for title because pageTitle is inherited from the default route. Most of the fields are simple inline types, except location, which a reference field to another content item. sitecore\definitions\routes\EventRoute.sitecore.js

import {
  CommonFieldTypes,
  SitecoreIcon
} from "@sitecore-jss/sitecore-jss-manifest";
import packageJson from '../../../package.json';

export default function (manifest) {
  manifest.addRouteType({
    name: "jss-sandbox-event-page",
    displayName: "JSS Sandbox Event",
    icon: SitecoreIcon.Microphone,
    fields: [
      {
        name: "description",
        type: CommonFieldTypes.RichText,
        displayName: "Event Description",
        required: false
      },
      {
        name: "startDate",
        type: CommonFieldTypes.DateTime,
        displayName: "Event Start Date",
        required: true,
        standardValue: "$now"
      },
      {
        name: "endDate",
        type: CommonFieldTypes.DateTime,
        displayName: "Event End Date",
        required: false,
        standardValue: "$now"
      },
      {
        name: "location",
        type: CommonFieldTypes.ItemLink,
        displayName: "Event Location",
        source: `dataSource=/sitecore/content/${packageJson.config.appName}/Content/locations`,
        required: false
      }
    ]
  });
}

Adding a new route

Using my custom event route type, I was ready to create routes for event pages. I created a directory structure based on my desired URL structure (domain\events\event-name) and added a yaml file for an event route. I also created content items for location tags.

data
	routes
		events
			sugcon-europe
				en.yml
	content
		locations
			amer
				en.yml
			apac
				en.yml
			emea
				en.yml

I assigned a custom route type to the sugcon-europe route by referencing jss-sandbox-event-page in the template property, and I assigned a location by referencing the location tag item id in the fields.location.id property.

data\routes\events\sugcon-europe\en.yml

id: event-sugcon-europe
template: jss-sandbox-event-page
fields:
  pageTitle: Sitecore User Group Conference - Europe
  description: A conference for Sitecore users and developers.
  startDate: '2019-04-04T08:00:00.000Z'
  endDate: '2019-04-05T17:00:00.000Z'
  location:
    id: locations-emea
placeholders:
  jss-main:
  - componentName: EventHeader

Adding a component for displaying and editing route-level fields

I needed a way to display route-level fields on event pages. I wasn’t sure how to achieve this yet, so I started off with an EventHeader component and hardcoded HTML.

jss scaffold EventHeader

src\components\EventHeader\index.js

import React from 'react';

const EventHeader = () => (
  <div>
    Header Component
  </div>
);

export default EventHeader;

Since this component was for rendering route fields, it didn’t need any fields of its own. sitecore\definitions\components\EventHeader.sitecore.js

import { SitecoreIcon, Manifest } from '@sitecore-jss/sitecore-jss-manifest';

export default function (manifest) {
  manifest.addComponent({
    name: 'EventHeader',
    displayName: 'Event Header',
    icon: SitecoreIcon.DocumentTag,
    fields: [],
  });
}

All route ancestors need language versions

At this point, I wanted to make sure my event routes were working before I added more events. I fired up my local JSS server using the jss start command… and got smacked with this error.

> [DICTIONARY] served in en
> [LAYOUT] Layout for route '/events/sugcon-europe' was not defined. Returning 404.

The problem was that I didn’t have a route definition (yaml file) for the parent directory. To resolve the error, I needed to add an en.yml file under data\routes\events. This makes sense from a Sitecore perspective - all ancestors of a page need to have a version in the current language, or the current page won’t load.

data
	routes
		events
			en.yml
			sugcon-europe
				en.yml

event header hardcoded

Using ‘withSitecoreContext ‘ to access route-level fields from a component

In JSS, values for route-level fields are defined in the route’s yaml/json file. By default, these values are not passed down to the route’s components’ props.

I inspected the props received by the EventHeader component in React developer tools, and saw that the rendering.fields property was populated with an empty collection. This made sense since EventHeader doesn’t define any fields.

event header props

So how can the component access route-level fields?

JSS provides a helper called withSitecoreContext for accessing route data. Import it into your component and use it like this.

import React from 'react';
import { withSitecoreContext } from '@sitecore-jss/sitecore-jss-react';

const ComponentName = ({ sitecoreContext }) => (
	...
);

export default withSitecoreContext()(ComponentName);

Adding sitecoreContext to EventHeader allowed me to display all the event page fields.

import React from 'react';
import { Text, RichText, DateField } from '@sitecore-jss/sitecore-jss-react';
import { withSitecoreContext } from '@sitecore-jss/sitecore-jss-react';

const EventHeader = ({ sitecoreContext: { route: { fields } } }) => (
  <div>
    <Text tag="h2" field={fields.pageTitle} />
    <RichText field={fields.description} />
    <DateField field={fields.startDate} render={date => date.toLocaleDateString()} />
    -
    <DateField field={fields.endDate} render={date => date.toLocaleDateString()} />
    <p>
      Location: <Text field={fields.location.fields.value} />
    </p>
  </div>
);

export default withSitecoreContext()(EventHeader);

event header

Other applications of ‘withSitecoreContext’

Notice that sitecoreContext has some other interesting properties like

  • sitecoreContext.pageEditing
  • sitecoreContext.site.name
  • sitecoreContext.route.templateId These properties can be utilized when you need your component to render different HTML depending on the context. For example, ensuring that error state messages are visible in Experience Editor mode so that authors can edit the error text.

Lessons Learned

  • A route definition (yaml/json file) needs to exists for all ancestors of a route. It’s ok if it’s blank; it just needs to exist. Otherwise, the route cannot be rendered.
  • The withSitecoreContext helper gives components access to route data like editor mode, template ID, and route fields.

Try It Yourself

To experiment with this code yourself, you can clone it locally

git clone -b topic/create-page-routes https://github.com/anastasiya29/jss-sandbox.git

Remember to install the jss cli if you haven’t already done so, and run npm install from the project root to download dependencies. After that, use jss start to launch the app in your browser in disconnected mode.

Bon Appétit!

© 2018-2020 Anastasiya FlynnDisclaimerRSS