Developer use cases

Learn actions you can take to work with project maps.

The following example demonstrates how to list all compositions that are in a published state using the project map client (there are some exceptions).

In this example Uniform uses Dynamic Catch All Routes from Next.js to render all pages in single place, from Home page, to deep multilevel pages.

/pages/[[...path]].js

import {
  CanvasClient,
  CANVAS_DRAFT_STATE,
  CANVAS_PUBLISHED_STATE,
} from '@uniformdev/canvas';

const canvasClient = new CanvasClient({
  apiKey: '[!!! UNIFORM API KEY !!!]',
  projectId: '[!!! UNIFORM PROJECT ID !!!]',
});

export async function getStaticProps(context) {
  const path = context?.params?.path;
  const pathString = Array.isArray(path) ? path.join('/') : path;
  const { preview } = context;

  const { composition } = await canvasClient.getCompositionByNodePath({
    projectMapNodePath: pathString ? `/${pathString}` : '/',
    state:
      process.env.NODE_ENV === 'development' || preview
        ? CANVAS_DRAFT_STATE
        : CANVAS_PUBLISHED_STATE,
  });

  if (!composition) {
    return { notFound: true };
  }

  return {
    props: {
      composition,
      preview,
    },
  };
}

An example to define the paths of the compositions to be static-generated:

// /pages/[[...path]].js
import { CanvasClient, CANVAS_DRAFT_STATE, CANVAS_PUBLISHED_STATE, } from '@uniformdev/canvas';
import { ProjectMapClient } from '@uniformdev/project-map';

const projectMapClient = new ProjectMapClient({
  apiKey: '[!!! UNIFORM API KEY !!!]',
  projectId: '[!!! UNIFORM PROJECT ID !!!]',
});

export const getStaticPaths = async () => {
  const { nodes } = await projectMapClient.getNodes({
      projectMapId: '[!!! UNIFORM PROJECT MAP ID !!!]',
  });

  return {
    paths: nodes?.filter((node) => node.compositionId).map((node) => node.path) ?? [],
    fallback: false,
  };
};

By using the link parameter on components, editors can set links to other nodes. This ensures valid URLs even when the linked node is moved to another location within the project map. How these links are rendered in the frontend application is up to the developer to decide and implement.

Here is an example of a custom InternalLink component that demonstrates how to render an internal link with the Link parameter using the Next.js Link component

/src/components/InternalLink.js

import Link from "next/link";
export default function InternalLink({
  title = "",
  internalLink = {},
  openInNewTab = false,
}) {
  // get the URL from the 'path' property of the link parameter
  // if no link is set just render an anchor link - change to fit your needs
  const url = internalLink.path?.length ? internalLink.path : "#";
  // render link using Next.js Link Component
  return (
    <Link href={url} target={openInNewTab ? "_blank" : "_self"}>
      {title}
    </Link>
  );
}

Usually sitemap generation is your web framework task (also more reliable because of dynamic pages exceptions). But you can use Project Map API directly to fetch whole tree (tree or flat structure) data to construct it your self.

Next.js framework provides three ways to achieve it:

  1. Manual static file upload - https://nextjs.org/learn/seo/crawling-and-indexing/xml-sitemaps
  2. Generate via getServerSideProps - https://nextjs.org/learn/seo/crawling-and-indexing/xml-sitemaps. This is the only option where you'd need to use Project Map API
  3. Preferred option: Generate via npm package next-sitemap.

The tree-like hierarchical structure of nodes in a project map make it easy to render navigational components using the project map client. Example use cases for navigation could be:

  • Global navigation: Use project maps to render a main navigation or footer navigation
  • Local navigation: Show links to sibling or child nodes of a specific node.
  • Breadcrumb navigation: Show the trail of parent nodes of a specific node (such as Home > Company > About us).

Advanced or custom navigation

If you need to control the structure or display, or need to enrich your navigation with content or personalized links, Uniform recommends you create custom Canvas components that represent your navigation UI. This gives you a high level of flexibility as you can leverage all Canvas capabilities. Use link parameters in your components to connect your links with the project map.

To get the list of the top level project map nodes (for example, for a global navigation), you can use ProjectMapClient.getNodes and set the depth parameter to 1:

const projectMapClient = new ProjectMapClient({
  apiKey: '[!!! UNIFORM API KEY !!!]',
  projectId: '[!!! UNIFORM PROJECT ID !!!]',
});

const response = await projectMapClient.getNodes({
  depth: 1,
});

To get the ancestors of a project map node (for example, for breadcrumb navigation), you can use ProjectMapClient.getNodes and set the includeAncestors parameter to true:

const projectMapClient = new ProjectMapClient({
  apiKey: '[!!! UNIFORM API KEY !!!]',
  projectId: '[!!! UNIFORM PROJECT ID !!!]',
});

const response = await projectMapClient.getNodes({
  path: '/my-category/my-subcategory/my-page',
  includeAncestors: true,
});