Skip to main content

Developer use cases

This section provides information on how to use project maps for common scenarios.

Get all pages from project map

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

In this particular example we are using Dynamic Catch All Routes from Next.JS that allows us to render all pages in single place, from Home page, to deep multilevel pages.

// /pages/[[]].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 !!!]',
const canvasClient = new CanvasClient({
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,
export async function getStaticProps(context) {
const slug = context?.params?.id;
const slugString = Array.isArray(slug) ? slug.join('/') : slug;
const { preview } = context;
const { composition } = await canvasClient.getCompositionByNodePath({
projectMapNodePath: slugString ? `\${slugString}` : '/',
process.env.NODE_ENV === "development" || preview
if (!composition) {
return { notFound: true };
return {
props: {
preview: preview,

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

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"}>

Generate sitemap.xml

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 -

  2. Generate via getServerSideProps - This is the only option where you'd need to use Project Map API

  3. Generate via npm package next-sitemap. Preferred option

Generate navigation from project map

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 (e.g. Home > Company > About us).
Advanced or custom navigation

If you need more control on how to structure, display or enrich your navigation with content or personalize links then we recommend creating custom Canvas components that represent your navigation UI.

This give you a high level of flexibiltiy as you can leverage all Canvas capabilities. Use Link parameters in your components to connect your links with the project map.


Code examples coming soon.

Dynamic pages

In a case like a commerce site, you may have thousands of links where some sort of identifier is used in the URL to determine the content to display. Products may be added and removed constantly. It's not practical or realistic to create a project map node for each possible URL.

In addition, the composition used for these URLs may be virtually identical, with the only difference being the data displayed in the components. Again, it is neither practical nor realistic to create a composition for each possible URL.

In cases like this you can use a "dynamic routing" approach similiar static site generators like Next.js and Vue work.


In the future, Uniform's project map will provide support for dynamic routing as a native feature. Stay tuned for details.

  1. In your project map, add a project map node that represents the dynamic part of the URL.


    The node name is not significant, but the path segment value is because it is the value your front-end application will use to determine which content to retrieve.

  2. In your front-end application, retrieve the composition:

    const client = new CanvasClient({
    apiKey: "[!!! YOUR API KEY !!!]",
    projectId: "[!!! YOUR PROJECT ID !!!]",

    const { composition } = await canvasClient.getCompositionByNodePath({
    projectMapId: "[!!! YOUR PROJECT MAP ID !!!]",
    projectMapNodePath: "/products/product-id",
  3. Your front-end application is responsible for determining the value of the path segment (i.e. product-id) in order to retrieve the appropriate data from the relevant content sources.

Get project map ID

The project map ID is used when fetching data using the Uniform API.

  1. In Uniform, navigate to Settings > Canvas Settings.
  2. Scroll to the section Add Project Map.
  3. The ID for each project map is displayed.

Filter nodes query by composition state

You can filter your results by composition state.

State IDPublish state

Filtering by state 0 will always result in the full data being returned in either tree or list format.

Query for published compositions


This will return a list of nodes:

import { ProjectMapClient } from '@uniformdev/project-map';
const projectMapClient = new ProjectMapClient({
apiKey: '[!!! UNIFORM API KEY !!!]',
projectId: '[!!! UNIFORM PROJECT ID !!!]',
const { nodes } = await projectMapClient.getNodes({path: '/', state: 64});
nodes.foreach((node) => {

You will receive all nodes in published state as well as all placeholder nodes in an array.


This will return a tree of published composition nodes:

import { ProjectMapClient } from '@uniformdev/project-map';
const projectMapClient = new ProjectMapClient({
apiKey: '[!!! UNIFORM API KEY !!!]',
projectId: '[!!! UNIFORM PROJECT ID !!!]',
const { tree } = await projectMapClient.getNodes({path: '/', state: 64, tree: true});

You will receive a tree structure of all nodes that don't represent a composition in draft state.


All children nodes of a composition node in draft state will be omited from the results.