Pages define all the URLs that exist on your project. You can see them as the only entry points for stuff to show under your domain. Only these URLs will show something, and all the rest will lead to a 404 error (unless there exists a redirect for it).

The pages and their URLs are a very important aspect of your project. It's important to think it through and make sure they have a logical structure. Although you can always change things, this can have consequences for SEO, so it's better to have it right the first time.

Page Tree

It's crucial to know that pages live in a tree structure. Although you see them in a flat table, in fact they are structured in a tree, with parents and children. When you create a new page, you must always choose a parent page.

This has consequences for the URLs of the pages (see further). Also, when you delete a page with children, the whole subtree will be removed. To help you see the consequences of removing a page, Devisto will always show you a list of the pages that will be deleted.

Page Slugs

Each page has a unique slug. Slugs are used in URLs, but are also helpful to fetch pages in your code, for example with the page() helper:

<a href="{{ page('about')->url() }}">About Us</a>

Page URLs

Each page in Devisto has a unique URL for each language. You can right-click on a page and choose Copy URL to put the URL of the page to your clipboard.


The URL of a page is composed by 2 parts:

  1. The first part is inherited from the parent page. Therefore, the URL of a page will always start with the URL of its parent.
  2. The second part is defined by the page itself. There are 3 options:
    • By default, the slug of the page will be used.
    • Optionally, you can enter a specific URL for one or more languages in the URL tab of the page.
    • Or, when the page is dynamic, this part will be defined by URL Field of the entries in the linked table.


The very first item you see in the page table, is the root of the tree. The root defines the starting point of all URLs for a specific language. The root of every language must be unique. The root cannot be deleted.

If you have 1 language, the URL of the root for that language will probably be /, so your URLs will look like this:

  • /
  • /about
  • /contact

When you have a second language (let's say French), you may choose to make the root of that language /fr which makes your URLs for French look something like this:

  • /fr
  • /fr/a-propos-de-nous
  • /fr/contact

Notice that the root you choose does not have to be the same as the slug of the language. So you can have a language with slug fr-be and still choose / or /fr as the URL root for that language.

When you have multiple languages, you may choose to give all languages a root URL. In this case, you won't have a / URL, so you'll have to create a redirect from / to your primary language URL.


Because the pages live in a tree structure, each page is in fact a subpage of another page (except for the root). Therefore, changing the URL of a page with children, will also impact the URL of its children and grandchildren. To illustrate this, let's say we have the following pages:

  • /
  • /about
  • /about/mission
  • /about/team

When we change the URL of the second page from /about to /about-us, the URLs of our pages will become:

  • /
  • /about-us
  • /about-us/mission
  • /about-us/team

Notice that, although we only changed the URL of one page, in fact the URLs of 3 pages have been changed!

Dynamic Pages

A dynamic page does not just have one URL, but generates multiple URLs, based on the entries of a data table.

When you make a page dynamic, you must do 2 things:

  • Choose a data table.
  • Optionally, check one or more scopes of that table.

Instead of just one URL per language, the page will now generate one URL per language and per entry found in the data table (after the checked scopes are applied).

To illustrate this, let's assume we have the following pages:

  • /
  • /products
  • /products/{product}

The last page here is a dynamic page, linked to the products table. Notice the brackets in the URL: this means that {product} will not be literally used in the URLs, but instead acts as a placeholder. What comes in place of this placeholder, is defined by the entries in the products table. Let's say we have 3 products in our table, then the actual URLs of our project will be:

  • /
  • /products
  • /products/product-1
  • /products/product-2
  • /products/product-3

What is used to replace the placeholder is defined by the URL Field property of the linked table. By default, this will be the slug field, but you're completely free to change this.

Nested Dynamic Pages

Dynamic pages can be nested, but require relations to work as expected. Let's say we have a product-oriented website with the following URLs:

  • /products
  • /products/{category}
  • /products/{category}/{subcategory}
  • /products/{category}/{subcategory}/{product}

In order for these URLs to work properly, the following relations must exist:

  • Products → Subcategories
  • Subcategories → Categories

Devisto will use these relations to compose the URLs and to fetch the correct entries when these URLs are accessed. In other words, all you have to do is create the dynamic pages, link them to the correct tables and make sure the necessary relations exist. The rest is automatically taken care of by Devisto. From then on, you can call url() on the pages and entries and you will always receive the correct URL.

Fields & Scopes

You can add extra fields and scopes to the pages, just like you would for the normal tables you create in Devisto. They work exactly the same as with data tables. See the sections Fields and Scopes for a detailed explanation.

Why would you do this? Let's say you have a website design with a hero section on the top of every page. In this case you can create 2 extra fields hero_title and hero_image, fill these in for every page, and use them in your master template:

@if (page()->hero_title && page()->hero_image)
    <div class="aspect-w-16 aspect-h-9">
        <x-img :src="page()->hero_image" />
        <div class="flex items-center justify-center">
            <h1>{{ page()->hero_title }}</h1>

Take a look at the Hero snippet for a more detailed example.


Devisto automatically generates a sitemap for you, including all static and dynamic URLs. You can access the sitemap by visiting the /sitemap.xml URL of your site.


Sometimes, conflicts happen when dealing with URLs. In such cases, Devisto will show a warning in the page table.

There can be several possible warnings:

  • Some pages share the same URL. Of course, only one page can be shown on every URL.
  • A redirect exists that matches a page URL. In that case the redirect takes precedence and the page will never be shown.
  • There is more than one dynamic page on the same level (so sharing the same parent). This is not supported.
  • The page has a protected URL, like /admin or /sitemap.xml.
  • The page is a nested dynamic page and there is no relationship found between the linked tables.

When a page has one of these warnings, a yellow triangle will be shown. Click the triangle to see the warnings.


For each page, you can choose a view from the Views section. When the URL of a page is accessed, that view will be rendered.

In the views, you can use several helpers to do your thing:

// Current page

// Get a specific page

// Get the URL of a page

// Get all pages

// Query by slug
pages()->where('slug', 'home')->first();
pages()->whereIn('slug', ['home', 'about', 'contact'])->get();

// With a custom field to add logic
pages()->where('in_main_nav', 1)->get();

// With a custom scope

// On dynamic pages, the entry() helper returns the current entry
@foreach (breadcrumbs() as $item)
    <a href="{{ $item->url() }}">{{ $item->title() }}</a>
Previous topic
← Preview
Next topic
Redirects →