GraphQL

Query Nodes

Query a menu’s links with GraphQL when your frontend needs to select the fields it receives. Start with a saved menu called Main Menu, handle mainMenu, enabled for the site you want to query.

Configure Schema Access

In Craft’s GraphQL → Schemas, edit the schema your client will use. Under Navigation, enable View menu - Main Menu, then save. Use View all menus only when that schema should read every menu. Menu access is separate from access to linked entry or asset data.

Follow Craft’s GraphQL setup (opens new window) to create an endpoint and choose a public schema or a token for a private schema. Test using that same schema in GraphiQL; the administrator’s full schema can hide missing permissions. Keep private tokens in server-side code.

If you request a node’s linked element, the schema must also permit its specific section, category group, volume, or product type. A menu grant alone does not grant access to that content.

Run the example below in GraphiQL with your intended schema. You should receive Main Menu’s root links and their direct children. If navigationNodes is unavailable, check the schema’s Navigation grants. If results are empty, check the saved handle, site, and enabled state.

Query a Menu

{
  navigationNodes(menuHandle: "mainMenu", level: 1) {
    title
    url
    children {
      title
      url
    }
  }
}

Use the singular navigationNode query to fetch one node by ID or UID.

Refine the Query

Use menuHandle to select the menu and level: 1 to start at its roots. For a secondary site, add its site handle, such as site: "french", to the query arguments. In addition to standard Craft element query arguments (opens new window), node queries support:

ArgumentDescription
menuHandleMenu handle
menuIdMenu ID
typeNode type class name(s)
withLinkedElementsBatch-load linked Craft elements (disables tree cache)
withNodeHierarchyWire parent/child in memory (null = auto on front-end)
withMenuBatch-load parent Menu elements (disables tree cache)
withProjectedChildrenInclude Dynamic projections; false to skip

Site and Language

  • site / siteId select which node variant to return (per-site titles, URLs, enabled state).
  • For element-backed nodes, linkedElementSiteId (per node-site row) selects which locale of the linked element supplies the URL — independent of the node's siteId.

In multisite setups, pass explicit site / siteId when the requested site is not the primary site. The language field on each node reflects the site's language, not a separate filter dimension.

Headless consumers should use the same site context as entry queries so URLs and enabled state match the front-end.

Each menu with a field layout can expose a {handle}_Menu GraphQL type for menu-level custom fields.

Projected Nodes

Dynamic children return as ProjectedNavigationNode with isProjected: true. See Projected Node.

Returned Nodes

The NodeInterface Interface

Stored and projected nodes implement NodeInterface. Alongside inherited Craft element and structure fields such as id, title, siteId, and level, the interface exposes the following Navigation fields. A ! marks a non-null value.

FieldTypeDescription
isProjectedBoolean!Whether the node is generated at read time rather than stored in the menu structure.
elementIdIntThe linked element’s ID.
menuIdIntThe owning menu’s ID.
menuHandleStringThe owning menu’s handle.
menuNameStringThe owning menu’s title.
typeStringThe node type’s PHP class name.
typeLabelStringThe display name of the node type.
classesStringAdditional CSS classes.
urlSuffixStringThe configured URL suffix.
customAttributes[NodeCustomAttribute]Additional attributes, each with attribute: String and value: String.
dataStringAdditional stored node data encoded as JSON; null for projected nodes.
newWindowStringThe open-in-new-window value, serialised as a GraphQL string.
urlStringThe node’s full URL.
nodeUriStringThe node’s URI.
children[NodeInterface]The node’s children, including Dynamic projections when enabled.
parentNodeInterfaceThe parent node, or null for a root node.
elementElementInterfaceThe linked element, subject to the active schema’s content permissions.

Projected nodes expose their synthetic id and uid, site information, URI and menu-relative level. Stored-element metadata such as dateCreated, dateUpdated, root, and structureId returns null. Settings they do not store, such as classes, urlSuffix, and customAttributes, return null or empty values. Use __typename to distinguish their ProjectedNavigationNode type. Custom node fields belong on the concrete type generated for the menu; inspect the active schema in GraphiQL for its exact fields.

The Node reference describes the PHP and Twig object. Its methods and properties are not automatically exposed as GraphQL fields.

Current-Page Information

Current-page helpers are available through navigationContext; NodeInterface does not expose current, active, or hasActiveChild fields. A GraphQL request does not automatically inherit the URL open in your frontend. For browser-side matching, follow Expose a Menu as JSON.

To identify the current menu branch or build a breadcrumb trail in your frontend, use the queries in Context & Breadcrumbs.