Page are used for discovery and navigation. You can manage them in the Pages section of your dashboard. They are used to assign URLs (routes) to inidivdual blocks. That way, you can use the routing mechanism of your app to fetch the content of a page.

Pages also handle the logic of redirects, conflict resolution and multi-language / localization.

Example

Most online shops have different types of pages - category pages, product pages, static content pages, etc. Each of them is uniquely identified by a URL (or a route). Now how do you know what type of content is assigned to that route? This is where pages come into play.

Imagine you have a match-all page at the root of your app. At that point, the only information available is the slug of the page (for example fr/femme/chemises). In Frontstack, every type of content is represented by a page type.

So after defining a page type for each page, you can simply fetch the page by its slug:

// Resolve the slug from the route
const { slug } = useRoute().params;

// Fetch the page using the slug
const { page } = await client.page(slug);

As you can see, the page object contains the page type - e.g. category, product, content, etc. - which you can use to determine the component to render, whereas data contains the actual data for the page.

Create a page

You can create a page by going to the Pages section and clicking on the “New Page” button.

Basic information

When creating a page, Frontstack will ask for three pieces of information:

  • Page name - a human readable name for the page that is also used to generated the identifier (page type)
  • Block - the structure of the page has to be a block - e.g. a list of products or a category
  • Slug - the field of the block that is used to generate the slug (e.g. name or breadcrumbs)

Frontstack uses this information to generate pages - which are technically stores as routes that point to a specific block record.

In the second step, you can define additional behavior for the page:

Action on withdrawal

Pages change, or at least, the blocks that were initially assigned to them may change. In commerce applications, you don’t want URLs to disappear, so you need to define what should happen when the underlying “truth” changes.

ActionDescription
302 Redirect to homeThe most common behavior, temporary redirect
301 Redirect to homePermanent redirect
Delete URLThe URL will be deleted and the page will be removed
Keep the URL and show a 404The URL will be kept, but the page will not be found

Conflict resolution

When Frontstack generates page links, it will use the configured slug to generate the URL. However, all URLs in the system must be unique. If a slug generation would result in a duplicate URL (for example, when two products have the same name), you can apply a conflict resolution strategy.

StrategyDescription
Append tokenSpecify a secondary token that will be appended to the slug to make it unique
DismissSkip the page and don’t create a link

Fetch a page

After creating a page, you can fetch it by its slug. Based on the page type, the data will be different.

const { page } = await client.page(slug);

The returned object will look like this:

{
  "data": {
    "slug": "gloves-0194bcdaa2d57dc4bb4a9f33ed6b718c",
    "title": "Gloves"
  },
  "type": "CategoryDetail",
  "block": "CategoryPage"
}

How you use the API is up to you, but we recommend using the type field to determine which page component to render. Frontstack also passes the underlying block name in the block field - so if you reuse an existing component, you can use this one as well. The data field contains the actual data for the page. Its structure depends on the block configuration.

Inherited listing settings

If a page is assigned to a listing block, in will inherit all listing settings from it. For example, if you create a page for a category that contains a listing of products, you can use the same API to filter, sort and paginate the result as you would do in the listing block directly.

Menus are used to manage navigation structures in your app. They are closely related to pages and routes.

Oftentimes you get the menu structure from the CMS or another external source. Frontstack doesn’t enforce any structure on the menus, but it tries to provide a few helpers to make it easier to work with them.

An app can have multiple menus - imagine a shop menu and a footer menu which may have different items:

- root
  - Shop
    - Home
    - Women
    - Men
    - Kids
    - Sale
  - Footer
    - About
    - Contact
    - Terms
    - Privacy

Frontstack will allow you to create a menu for each of these and fetch them in your app,

const shopMenu = await client.menu("shop");
const footerMenu = await client.menu("footer");

Create a menu

To create a menu, go to the Pages section and click on the “Create Menu” button.

Frontstack can automatically create a menu from any hierarchical Storage type that has an associated Page. A storage type is hierarchical if it has a self-referencing field (e.g. parent or parentId). A page is required, because the menu item will link to it.

Name

Define a name for the menu, which will be used to fetch the menu from the API.

const shopMenu = await client.menu("shop"); // "shop" is the name

Page and hierarchy

Configure the menu items, by selecting the page type that you want to build a menu for (usually those will be your category pages) and the field that is used to determine the hierarchy.

Frontstack stores categories in a flat structure, so you need to select the field that is used to determine the hierarchy:

[
  {
    "id": "5a6b7c8d9e",
    "name": "root",
    "parentId": null
  },
  {
    "id": "d87654321",
    "name": "Shop",
    "parentId": "5a6b7c8d9e"
  },
  {
    "id": "1a2b3c4d",
    "name": "Women",
    "parentId": "d87654321"
  }
]

In this case, the hierarchy is determined by the parentId field and the resulting menu will be:

- root
  - Shop
    - Women

Root item

You can create multiple submenus from the same page type - for example, when both of your menus are based on Category pages, but you want to have a separate menu for each of them.

- root
  - Shop ["shop" alias]
    - Home
    - Women
    - Men
    - Kids
    - Sale
  - Footer ["footer" alias]
    - About
    - Contact
    - Terms
    - Privacy

By selecting a root item, the resulting API will only include the pages that are directly under the selected item. You could achieve the same effect by just passing the corresponding ID to the API call, but that would require your frontend to know the ID of the corresponding menu item.

const footerMenu = await client.menu("shop", { rootId: "1a2b3c4d" });
// "1a2b3c4d" is the ID of the footer menu item

can be replaced with

const footerMenu = await client.menu("footer");

Fetch a menu

Fetching a menu is done by calling the menu function with the alias you defined when creating the menu.

const shopMenu = await client.menu("shop");

If you only need a specific submenu (for example for an expandable menu), you can pass the rootId option to the menu function:

const footerMenu = await client.menu("footer", { rootId: "1a2b3c4d" });

Sometimes, you may need to fetch only the first level of the menu. You can achieve this by passing the depth option to the menu function:

const footerMenu = await client.menu("footer", { depth: 1 });