Solving development challenges - Dynamic Breadcrumbs

Eise OlusegunEise Olusegun
5 min read

As I start my journey with the HNG Internship), I'm excited to share a recent backend challenge I tackled.

Background

A couple of months ago, around the start of 2024, I started learning Laravel and PHP despite the abundant Twitter memes (although I am still hoping for the PHP Lamborghini). Before that, I was used to Python for the backend, using frameworks like FastAPI and Django, and also a bit of JavaScript with frontend tools like React. During this period, I gradually came to like Laravel enough to choose it on my own for a client's project.

The complete tech stack was almost a no-brainer: a Laravel backend and a React client connected using InertiaJS. Although Livewire was a consideration, I ultimately decided on the devil I already knew, React, and I had only heard good things about Inertia.

The Problem - A sprinkle of breadcrumbs

The design required breadcrumb navigation. So, I had to dynamically display the breadcrumb navigation on the client and have them link to the correct page. Knowing the Laravel ecosystem, I quickly went for a package hunt, but most of what I stumbled across were not intended for Inertia and had no clear method of integration. Until I found inertia-breadcrumbs, but for whatever reason (likely skill issue), I couldn't get it to work as intended. It was then clear that no package was coming to bail me out this time; I had to understand the problem.

The challenge was generating the breadcrumbs that accurately reflected the user's navigation path through the complex application structure and making it always available on the client without having to manually pass the breadcrumbs to the frontend while rendering each page.

The Journey to a Solution - Following the trail

Step 1: Understanding the Problem, I needed a system that would:

  • Extract the route information

  • Generate breadcrumbs information using the route info

  • Quietly pass the breadcrumbs back to the client via inertia

Step 2: Customizing the Middleware

What I needed fit perfectly into a middleware's role.

If you have setup your project using Laravel breeze starter kit, you should have;

app/Http/Middleware


class HandleInertiaRequests extends Middleware
{
    /**
     * The root template that is loaded on the first page visit.
     *
     * @var string
     */
    protected $rootView = 'app';

    /**
     * Determine the current asset version.
     */
    public function version(Request $request): string|null
    {
        return parent::version($request);
    }

    /**
     * Define the props that are shared by default.
     *
     * @return array<string, mixed>
     */
    public function share(Request $request): array
    {
        return [
            ...parent::share($request),
            'auth' => [
                'user' => $request->user(),
            ],
            'ziggy' => fn () => [
                ...(new Ziggy)->toArray(),
                'location' => $request->url(),
            ],
        ];
    }
}

All I needed was to define a private new method

private function extractBreadcrumbs(Request $request)
    {
        $breadcrumbs = [];
        $path = '';
        $segments = $request->segments();
        // The URL segments of the current request are retrieved using 
        // $request->segments() and stored in $segments.

        foreach ($segments as $segment) {
            //For each segment, the $path is updated by appending the
            //segment to it, prefixed with a '/'.    
            $path .= '/' . $segment;
            // this retrieves the route object using the path
            $route = Route::getRoutes()->match(Request::create($path));
            // this 'selected' flag would be used in the client 
            //to maybe disable the link for the last breadcrumb 
            $selected = false;
            if ($segment === end($segments)) {
                $selected = true;
            }
            if ($route) {
                $name = $route->getName();
                if ($name) {
                    $breadcrumbs[] = [
                        'name' => $name,
                        'label' => $segment,
                        'url' => $path,
                        'selected' => $selected,
                    ];
                }
            }
        }

        return $breadcrumbs;
    }

This middleware iterates through the URL segments, matches them to routes, and builds a breadcrumb array.

Now finally we modify the share method, to pass the breadcrumbs to the client


class HandleInertiaRequests extends Middleware
{

    public function share(Request $request): array
    {
        return [
            ...parent::share($request),
            'auth' => [
                'user' => $request->user(),
            ],
            'ziggy' => fn () => [
                ...(new Ziggy)->toArray(),
                'location' => $request->url(),
            ],
            // we pass the breadcrumbs to the client
            'breadcrumbs' => $this->extractBreadcrumbs($request),
        ];
    }
}
  • Step 3: Accessing the breadcrumb from the frontend

Now thanks to the inertia middleware, the breadcrumbs are now available as props to our page components and can be accessed as simply as this

export default function Dashboard({ breadcrumbs }) {
    console.log(breadcrumbs)
    return (
        ...
    );
}

// or

import { usePage } from "@inertiajs/react";

export default function Dashboard() {
    const breadcrumbs = usePage().props.breadcrumbs;
    console.log(breadcrumbs)
    return (
        ...
    );
}

My implement always pass the breadcrumbs as props to the pages in this format

// Example if path /admin/products

[
    {
        "name": "admin.index",
        "label": "admin",
        "url": "/admin",
        "selected": false
    },
    {
        "name": "products.index",
        "label": "products",
        "url": "/admin/products",
        "selected": true
    }
]

Note: The name field can be used to directly access the Laravel route using Ziggy's route helper like route('admin.index') , Ziggy is configured by default in breeze Laravel starter kit for inertia.

Outro

The final solution provided a robust, dynamic breadcrumb system that enhanced user navigation and improved the overall UX of the project. I was satisfied with the result.

I always find it exciting when I can solve challenges like these. That's why I enrolled in the HNG11 internship. With its fast-paced approach and diverse tasks, the experience is beneficial for both newcomers and intermediate tech enthusiasts. Over 11 cohorts, HNG has built a strong reputation and a large community. If you would like to hire some of their experts, visit their hire page.

0
Subscribe to my newsletter

Read articles from Eise Olusegun directly inside your inbox. Subscribe to the newsletter, and don't miss out.

Written by

Eise Olusegun
Eise Olusegun