Single CMS Page
About
The page(id:) query retrieves a single CMS (Content Management System) page. Use this query to:
- Render a specific CMS page (e.g. About Us, Privacy Policy) in the storefront
- Retrieve the full HTML content of a page for display
- Access SEO metadata (meta title, description, keywords) for a specific page
- Fetch locale-specific translation data for a page
- Build dynamic page routes using
urlKey - Validate whether a page exists before rendering
The query returns the page with translation — the single translation for the resolved locale — and translations, a cursor-paginated connection of every stored translation for that page. Use the pages query to list all available pages.
The id argument accepts both the IRI form (/api/shop/pages/1) and a plain numeric ID (1); both resolve to the same page.
Locale Handling
The locale is resolved per request, in this order:
- The
X-Localeheader, when the supplied code is one of the locales enabled on the current channel. - The channel's default locale, when
X-Localeis absent or names a locale the channel does not have enabled.
The two translation fields then behave as follows:
- Resolved locale —
translationcarries the record for the locale resolved above. - Fallback — when the page has no translation for that locale,
translationfalls back to the application's fallback locale (enby default). If neither exists,translationisnull. - Reported locale — the
localefield insidetranslationalways states which locale the returned content actually came from. - All languages —
translationsis unaffected by the request locale. It returns every stored translation, so a client can cache all languages of a page in one call.
Arguments
| Argument | Type | Required | Description |
|---|---|---|---|
id | ID! | ✅ Yes | Identifier of the page — IRI form (/api/shop/pages/1) or numeric (1). |
Possible Returns
Page Fields
| Field | Type | Description |
|---|---|---|
id | ID! | IRI-style unique identifier (e.g. /api/shop/pages/1). |
_id | Int! | Numeric database ID. |
layout | String | Page layout template name. null when no layout is assigned. |
createdAt | String | ISO 8601 timestamp of when the page was created. |
updatedAt | String | ISO 8601 timestamp of when the page was last updated. |
translation | PageTranslation | Translation for the resolved locale. null when the page has no translation at all. |
translations | PageTranslationCursorConnection | Cursor connection over every stored translation for this page. |
PageTranslation Fields
Returned by translation and by each translations edge node.
| Field | Type | Description |
|---|---|---|
id | ID! | IRI-style ID of the translation record. |
_id | Int! | Numeric translation record ID. |
pageTitle | String! | Display title of the CMS page. |
urlKey | String! | URL slug used to access the page (e.g. about-us). |
htmlContent | String | Full HTML body content of the page. |
metaTitle | String | SEO meta title tag. |
metaDescription | String | SEO meta description tag. |
metaKeywords | String | SEO meta keywords. |
locale | String! | Locale code this translation belongs to (e.g. en, fr). |
cmsPageId | String! | ID of the page this translation belongs to. |
Translations Connection Fields
| Field | Type | Description |
|---|---|---|
edges[].node | PageTranslation | A single translation record — fields as above. |
edges[].cursor | String! | Cursor for this edge, used as after on the next request. |
pageInfo.hasNextPage | Boolean! | Whether more translations follow the current slice. |
pageInfo.hasPreviousPage | Boolean! | Whether translations precede the current slice. |
pageInfo.startCursor | String | Cursor of the first edge in the slice. |
pageInfo.endCursor | String | Cursor of the last edge in the slice. |
totalCount | Int | Total translations stored for this page. |
Use Cases
1. Render a CMS Page
Fetch a specific page by ID and render its htmlContent on a dedicated page route in the storefront.
2. SEO Head Tags
Use metaTitle, metaDescription, and metaKeywords from the translation to populate <meta> tags dynamically.
3. Page Validation
Query a page before rendering to check whether it exists; handle a null page with a 404 page.
4. Multi-language Content
Send X-Locale to fetch a specific language, or read the whole translations connection to build a language switcher without one request per locale.
5. Dynamic Page Routing
Use urlKey to implement client-side routing so users access pages via human-readable URLs (e.g. /about-us).
Best Practices
- Send the storefront key — every storefront GraphQL request needs the
X-STOREFRONT-KEYheader; without it the request is rejected before the query runs - Pick one ID form and keep it — both the IRI (
/api/shop/pages/1) and the numeric (1) form work; the IRI form matches whatidreturns, so round-tripping a response value needs no conversion - Check
data.pageanderrorstogether — an unknown or malformed ID returnsdata.pageasnullwith an entry inerrors; treat that as a 404 rather than an outage - Read
translation.locale— it reports the locale the content actually came from, which differs from the requested one whenever the fallback applies - Sanitize
htmlContent— the field is raw HTML authored in the admin panel; sanitize it before injecting into the DOM - Cache per page and locale — cache keyed by page ID alone will serve the wrong language once a second locale is enabled
- Fetch only needed fields — omit
htmlContentandtranslationswhen only the title and URL are needed; both grow the response several times over, andhtmlContentcarries a full HTML body - Use
urlKeyfor links — build storefront URLs fromurlKeyfor SEO-friendly, human-readable routes
Related Resources
- Get All CMS Pages - Query all CMS pages
- Theme Customisations - Query storefront theme customisations
- Pagination Guide - Cursor pagination documentation
- Shop API Overview - Overview of Shop API resources

