Skip to content

Start building your next app or website β€” with AI, right in your browser.

Visit devdojo.com
Docs
Page-Based Routing

Sites 04 / 13

Page-Based Routing

Add a file to resources/views/pages/, get a URL on your site. That's the whole routing system.

Page-Based Routing

Creating pages for your website is as simple as adding a new file to your resources/views/pages/ directory. The file's name is its URL β€” that's the whole routing system. No route files, no configuration, nothing to keep in sync. πŸ—ΊοΈ

How URLs work

File URL
resources/views/pages/index.blade.php /
resources/views/pages/about.blade.php /about
resources/views/pages/pricing.blade.php /pricing
resources/views/pages/blog/hello.blade.php /blog/hello
resources/views/pages/contact/index.blade.php /contact

Two rules cover everything:

  1. Folders become URL segments. pages/docs/setup.blade.php is served at /docs/setup.
  2. index.blade.php is the folder's front door. pages/index.blade.php is your homepage; pages/contact/index.blade.php answers at /contact.

Two ways to say /contact. Both pages/contact.blade.php and pages/contact/index.blade.php answer at /contact β€” use whichever structure you prefer. If both exist, the flat file wins.

Visiting a URL with no matching page shows a friendly 404 in the preview that tells you exactly which file to create. Want a custom one? Add resources/views/pages/404.blade.php.

Creating a page

Open the page chip at the canvas's top-left (or the rail's Pages panel) and click New Page. Type a name β€” contact is enough; the .blade.php is added for you β€” and the page is created already wrapped in your layout, with the preview pointed at its URL.

The switcher doubles as your map: each row shows the page's URL, and jumps the preview between pages at any time. Need to change a page's URL, title, or description later β€” or duplicate or delete it? That's the Page tab in the sidebar β€” or open Pages in the top bar for the full-screen view: every page listed on the left, its settings and a live preview on the right.

Switch to Code, hit the new-file button in the file tree toolbar, and create a file under resources/views/pages/:

<!-- resources/views/pages/contact.blade.php -->
<x-layouts.main title="Contact">

    <section class="mx-auto max-w-3xl px-6 py-24">
        <h1 class="text-4xl font-bold">Get in touch</h1>
        <p class="mt-4 text-zinc-500">We'd love to hear from you.</p>
    </section>

</x-layouts.main>

Type a path to create nested pages: blog/hello.blade.php lives at /blog/hello.

A couple of naming details worth knowing:

  • Names are lowercased and spaces become dashes (My Page β†’ my-page.blade.php).
  • Nested pages are just a path: blog/hello.blade.php.

Everything between the <x-layouts.main> tags is your page content β€” it gets placed into the layout's {{ $slot }}.

Stay inside the tags. When a page uses a layout, keep all of its content inside the <x-layouts.main> tags β€” that's what gets the document shell. (More in Layouts.)

Dynamic pages: one file, many URLs

A blog shouldn't need a new page file for every post. Name a page [collection.field].blade.php and it becomes a dynamic page: it serves one URL for every entry in that collection, matched on the field you name β€” the same idea as Laravel Folio's route parameters, powered by your collection data.

File Collection Serves
pages/post/[post.slug].blade.php collections/post.json /post/{slug} for every entry
pages/work/[sites.slug].blade.php collections/sites.json /work/{slug} for every entry

Inside the page, the collection's variable is the matched entry β€” a single object, not the whole array. Give each entry a slug plus whatever the page needs, with the body HTML in a content field:

// resources/data/collections/post.json
[
    {
        "title": "Writing Less, Achieving More",
        "slug": "writing-less-achieving-more",
        "description": "On minimalist code β€” fewer lines, clearer intent.",
        "dateFormatted": "June 11th, 2026",
        "readTime": "4 min read",
        "link": "/post/writing-less-achieving-more",
        "content": "<h2>Introduction</h2><p>In a world obsessed with more…</p>"
    }
]
<!-- resources/views/pages/post/[post.slug].blade.php -->
<x-layouts.post :title="$post->title" :description="$post->description">

    {!! $post->content !!}

</x-layouts.post>

Visiting /post/writing-less-achieving-more binds $post to that entry; {!! … !!} outputs the content HTML unescaped. Publishing a new post is now just adding an entry to the collection β€” no new page file. Your listing pages keep working unchanged: there the collection variable is still the whole array, and each entry's link points at its dynamic URL.

Need the whole collection inside a dynamic page β€” a docs sidebar, a "more posts" list, prev/next links? Since the collection's own variable now holds just the matched entry, the full array is always available as $entries:

<nav>
    @foreach ($entries as $item)
        <a href="{{ $item->link }}">{{ $item->title }}</a>
    @endforeach
</nav>

{!! $post->content !!}

(That makes entries a reserved name inside dynamic pages.)

A few rules worth knowing:

  • Static pages win. If pages/post/special.blade.php exists, /post/special serves it β€” the dynamic page only answers for slugs without a file of their own.
  • Unknown slugs 404, same as any missing page.
  • Exports and publishing fan out automatically: every entry ships as its own post/{slug}/index.html document, and each appears in your generated sitemap.
  • Opening the [post.slug] page itself in the builder previews the first entry of the collection.

Page settings: title, description, and URL

Every page has three settings you'll want to get right for search engines and browser tabs β€” its title, meta description, and URL slug. Open the page chip's dropdown (or the Pages panel) and click the gear on a page's row β€” its settings slide in over the open panel, with all three fields (plus the page's layout, duplicate, and delete). Under the hood, the title and description are the attributes on the page's layout tag:

<x-layouts.main title="Get in touch" description="Reach the team β€” we reply within a day.">

Rename a page's slug and its file moves to match β€” contact becomes contact.blade.php at /contact. Your homepage (index.blade.php) is special and can't be renamed away from /; everything else is yours to move.

Linking between pages

Link pages with a plain root-relative anchor β€” no helper needed:

<a href="/about">About</a>
<a href="/">Home</a>

A leading / always points at your site's root, and the builder keeps those links working in the preview, on your published site, and in exports. Use real <a href> anchors for every link β€” the preview relies on them, so navigating with JavaScript (onclick="window.location=…") won't work.

Making a whole card clickable? Wrap the card's markup in a single <a href="/…"> and use <span>s for the inner text β€” anchors can't be nested inside other anchors.

Reusing a page at two URLs

Want the same page to answer at a second URL? Blade pages don't have a special "reuse" tag β€” just keep the shared parts in a component and include it from both pages:

<!-- resources/views/pages/start.blade.php -->
<x-layouts.main title="Start here">
    <x-sections.welcome/>
</x-layouts.main>
<!-- resources/views/pages/index.blade.php -->
<x-layouts.main title="Home">
    <x-sections.welcome/>
</x-layouts.main>

Now /start and / both render components/sections/welcome.blade.php. Update the component once, and both pages stay in sync β€” no copy-paste drift. (More on this in Components.)

Next up

Pages get their shared shell from Layouts.

Β© 2026 DevDojo Edit this page