Be part of JetBrains PHPverse 2026 on June 9 – a free online event bringing PHP devs worldwide together.

AndrewL64's avatar

Links to routes not working

Hello, Just started experimenting with Laravel 9 (Inertia + React) and wanted to set up user roles. I tried following the steps in this guide and got it working but now the login/register links on my Navbar layer stops working. The logout link works fine however.

Directly typing and going to 127.0.0.1/login or 127.0.0.1/register works fine and the routes load as they should but clicking the links doesn't do anything (the progress bar moves but after it fills up, nothing happens).

What is causing this? Snippets of my auth.php, web.php, navbar layer and the authenticated/guest layers provided below. Other than the changes shown in the guide and the following files, everything else is default:

auth.php

Route::middleware('guest')->group(function () {
    Route::get('register', [RegisteredUserController::class, 'create'])
                ->name('register');

    Route::post('register', [RegisteredUserController::class, 'store']);

    Route::get('login', [AuthenticatedSessionController::class, 'create'])
                ->name('login');

    Route::post('login', [AuthenticatedSessionController::class, 'store']);

    Route::get('forgot-password', [PasswordResetLinkController::class, 'create'])
                ->name('password.request');

    Route::post('forgot-password', [PasswordResetLinkController::class, 'store'])
                ->name('password.email');

    Route::get('reset-password/{token}', [NewPasswordController::class, 'create'])
                ->name('password.reset');

    Route::post('reset-password', [NewPasswordController::class, 'store'])
                ->name('password.update');
});

Route::middleware('auth')->group(function () {
    Route::get('verify-email', [EmailVerificationPromptController::class, '__invoke'])
                ->name('verification.notice');

    Route::get('verify-email/{id}/{hash}', [VerifyEmailController::class, '__invoke'])
                ->middleware(['signed', 'throttle:6,1'])
                ->name('verification.verify');

    Route::post('email/verification-notification', [EmailVerificationNotificationController::class, 'store'])
                ->middleware('throttle:6,1')
                ->name('verification.send');

    Route::get('confirm-password', [ConfirmablePasswordController::class, 'show'])
                ->name('password.confirm');

    Route::post('confirm-password', [ConfirmablePasswordController::class, 'store']);

    Route::post('logout', [AuthenticatedSessionController::class, 'destroy'])
                ->name('logout');
});

web.php

use App\Http\Controllers\Auth\RedirectAuthenticatedUsersController;
use Illuminate\Foundation\Application;
use Illuminate\Support\Facades\Route;
use Inertia\Inertia;

Route::get('/', function () {
    return Inertia::render('Welcome', [
        'canLogin' => Route::has('login'),
        'canRegister' => Route::has('register'),
        'laravelVersion' => Application::VERSION,
        'phpVersion' => PHP_VERSION,
    ]);
});

// Route::get('/dashboard', function () {
//     return Inertia::render('Dashboard');
// })->middleware(['auth', 'verified'])->name('dashboard');

Route::group(['middleware' => 'auth'], function() {
    Route::inertia('/dashboard', 'Dashboard')->name('dashboard');

    Route::get("/redirectAuthenticatedUsers", [RedirectAuthenticatedUsersController::class, "home"]);

    Route::group(['middleware' => 'checkRole:admin'], function() {
        Route::inertia('/adminDashboard', 'AdminDashboard')->name('adminDashboard');
    });
    Route::group(['middleware' => 'checkRole:user'], function() {
        Route::inertia('/userDashboard', 'UserDashboard')->name('userDashboard');
    });
    Route::group(['middleware' => 'checkRole:guest'], function() {
        Route::inertia('/guestDashboard', 'GuestDashboard')->name('guestDashboard');
    });
});

Navbar.jsx

import React, { useState } from 'react';
import ApplicationLogo from '@/Components/ApplicationLogo';
import { Link, usePage } from '@inertiajs/inertia-react';
import Dropdown from '@/Components/Dropdown';
import ResponsiveNavLink from '@/Components/ResponsiveNavLink';

export default function Navbar({ children }) {
    const { auth } = usePage().props
    const [showingNavigationDropdown, setShowingNavigationDropdown] = useState(false);
    return (
        <>
            <nav className="bg-white border-b border-gray-100">
                <div className="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8">
                    <div className="flex justify-between h-16">
                        <div className="flex">
                            <div className="shrink-0 flex items-center">
                                <Link href="/">
                                    <ApplicationLogo className="block h-9 w-auto text-gray-500" />
                                </Link>
                                <span className="font-semibold text-xl tracking-tight">DrewL Dashboard</span>
                            </div>
                        </div>

                        <div className="hidden sm:flex sm:items-center sm:ml-6">
                            <div className="ml-3 relative">
                            <Dropdown>
                                    <Dropdown.Trigger>
                                        <span className="inline-flex rounded-md">
                                            <button
                                                type="button"
                                                className="inline-flex items-center px-3 py-2 border border-transparent text-sm leading-4 font-medium rounded-md text-gray-500 bg-white hover:text-gray-700 focus:outline-none transition ease-in-out duration-150"
                                            >
                                                {auth.user ? (
                                                    auth.user.name
                                                ) : (
                                                    <>
                                                        Log In/Register
                                                    </>
                                                )}
                                                <svg
                                                    className="ml-2 -mr-0.5 h-4 w-4"
                                                    xmlns="http://www.w3.org/2000/svg"
                                                    viewBox="0 0 20 20"
                                                    fill="currentColor"
                                                >
                                                    <path
                                                        fillRule="evenodd"
                                                        d="M5.293 7.293a1 1 0 011.414 0L10 10.586l3.293-3.293a1 1 0 111.414 1.414l-4 4a1 1 0 01-1.414 0l-4-4a1 1 0 010-1.414z"
                                                        clipRule="evenodd"
                                                    />
                                                </svg>
                                            </button>
                                        </span>
                                    </Dropdown.Trigger>

                                    {auth.user ? (
                                        <Dropdown.Content>
                                            <Dropdown.Link href={route('logout')} method="post" as="button">
                                                Log Out
                                            </Dropdown.Link>
                                        </Dropdown.Content>
                                    ) : (
                                        <Dropdown.Content>
                                            <Dropdown.Link href={route('login')} as="button">
                                                Log in
                                            </Dropdown.Link>
                                            <Dropdown.Link href={route('register')} as="button">
                                                Register
                                            </Dropdown.Link>
                                        </Dropdown.Content>
                                    )}
                                    
                                </Dropdown>
                            </div>
                        </div>

                        <div className="-mr-2 flex items-center sm:hidden">
                            <button
                                onClick={() => setShowingNavigationDropdown((previousState) => !previousState)}
                                className="inline-flex items-center justify-center p-2 rounded-md text-gray-400 hover:text-gray-500 hover:bg-gray-100 focus:outline-none focus:bg-gray-100 focus:text-gray-500 transition duration-150 ease-in-out"
                            >
                                <svg className="h-6 w-6" stroke="currentColor" fill="none" viewBox="0 0 24 24">
                                    <path
                                        className={!showingNavigationDropdown ? 'inline-flex' : 'hidden'}
                                        strokeLinecap="round"
                                        strokeLinejoin="round"
                                        strokeWidth="2"
                                        d="M4 6h16M4 12h16M4 18h16"
                                    />
                                    <path
                                        className={showingNavigationDropdown ? 'inline-flex' : 'hidden'}
                                        strokeLinecap="round"
                                        strokeLinejoin="round"
                                        strokeWidth="2"
                                        d="M6 18L18 6M6 6l12 12"
                                    />
                                </svg>
                            </button>
                        </div>
                    </div>
                </div>

                <div className={(showingNavigationDropdown ? 'block' : 'hidden') + ' sm:hidden'}>
                    <div className="pt-4 pb-1 border-t border-gray-200">
                        <div className="px-4">
                            <div className="font-medium text-base text-gray-800">Welcome</div>
                            <div className="font-medium text-sm text-gray-500">Login now</div>
                        </div>

                        <div className="mt-3 space-y-1">
                            {auth.user ? (
                            <ResponsiveNavLink href={route('logout')} as="button" method="post" className="inline-block text-sm px-4 py-2 leading-none border rounded text-white border-white hover:border-transparent hover:text-teal-500 hover:bg-white mt-4 lg:mt-0">
                                Log out
                            </ResponsiveNavLink>
                            ) : (
                            <>
                            <ResponsiveNavLink href={route('register')} as="button" method="post" className="inline-block text-sm px-4 py-2 leading-none border rounded text-white border-white hover:border-transparent hover:text-teal-500 hover:bg-white mt-4 lg:mt-0">
                                Register
                            </ResponsiveNavLink>
                            <ResponsiveNavLink href={route('login')} as="button" method="post" className="inline-block text-sm px-4 py-2 leading-none border rounded text-white border-white hover:border-transparent hover:text-teal-500 hover:bg-white mt-4 lg:mt-0">
                                Log in
                            </ResponsiveNavLink>
                            </>
                            )}
                        </div>
                    </div>
                </div>
            </nav>
            {children}
        </>
    );
}

Authenticated.jsx

import React from 'react';
import Navbar from '@/Layouts/Shared/Navbar.jsx';

export default function Authenticated({ auth, header, children }) {
    return (
        <Navbar>
            <div className="min-h-screen bg-gray-100">
                {header && (
                    <header className="bg-white shadow">
                        <div className="max-w-7xl mx-auto py-6 px-4 sm:px-6 lg:px-8">{header}</div>
                    </header>
                )}

                <main>{children}</main>
            </div>
        </Navbar>
    );
}

Guest.jsx

import React from 'react';
import ApplicationLogo from '@/Components/ApplicationLogo';
import { Link } from '@inertiajs/inertia-react';
import Navbar from '@/Layouts/Shared/Navbar.jsx';

export default function Guest({ children }) {
    return (
        <Navbar>
            <div className="min-h-screen bg-gray-100">
                <div className="min-h-screen flex flex-col sm:justify-center items-center pt-0 bg-gray-100">
                    <div>
                        <Link href="/">
                            <ApplicationLogo className="w-20 h-20 fill-current text-gray-500" />
                        </Link>
                    </div>
                    <div className="w-full sm:max-w-md mt-6 px-6 py-4 bg-white shadow-md overflow-hidden sm:rounded-lg">
                        {children}
                    </div>
                </div>
            </div>
        </Navbar>
    );
}
0 likes
9 replies
Sinnbeck's avatar

Check the browser console for errors.

Are you using ziggy for importing the routes ?

route('register')
AndrewL64's avatar

@Sinnbeck

Check the browser console for errors.

There are no errors in the console.

Are you using ziggy for importing the routes ?

Yes, Ziggy is included with Breeze + Inertia I think since I am using href={route('login')} and I remember finding a reference to ziggy in one of the files but I forgot which one. Hold on, let me check.

AndrewL64's avatar

Found it in middleware/HandleInertiaRequests.php:

use Tightenco\Ziggy\Ziggy;

class HandleInertiaRequests extends Middleware
{

    // other functions

    public function share(Request $request)
    {
        return array_merge(parent::share($request), [
            'auth' => [
                'user' => $request->user(),
            ],
            'ziggy' => function () use ($request) {
                return array_merge((new Ziggy)->toArray(), [
                    'location' => $request->url(),
                ]);
            },
        ]);
    }
}
Sinnbeck's avatar

@AndrewL64 Can you show the ResponsiveNavLink component ? And you RouteServiceProvider.php ?

AndrewL64's avatar

@Sinnbeck I have the default ResponsinveNavLink file and the problem happens with just <Link/> as well:

ResponsinveNavLink:

import React from 'react';
import { Link } from '@inertiajs/inertia-react';

export default function ResponsiveNavLink({ method = 'get', as = 'a', href, active = false, children }) {
    return (
        <Link
            method={method}
            as={as}
            href={href}
            className={`w-full flex items-start pl-3 pr-4 py-2 border-l-4 ${
                active
                    ? 'border-indigo-400 text-indigo-700 bg-indigo-50 focus:outline-none focus:text-indigo-800 focus:bg-indigo-100 focus:border-indigo-700'
                    : 'border-transparent text-gray-600 hover:text-gray-800 hover:bg-gray-50 hover:border-gray-300'
            } text-base font-medium focus:outline-none transition duration-150 ease-in-out`}
        >
            {children}
        </Link>
    );
}

RouteServiceProvider.php:

namespace App\Providers;

use Illuminate\Cache\RateLimiting\Limit;
use Illuminate\Foundation\Support\Providers\RouteServiceProvider as ServiceProvider;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\RateLimiter;
use Illuminate\Support\Facades\Route;

class RouteServiceProvider extends ServiceProvider
{
    /**
     * The path to the "home" route for your application.
     *
     * Typically, users are redirected here after authentication.
     *
     * @var string
     */
    public const HOME = '/redirectAuthenticatedUsers';

    /**
     * Define your route model bindings, pattern filters, and other route configuration.
     *
     * @return void
     */
    public function boot()
    {
        $this->configureRateLimiting();

        $this->routes(function () {
            Route::middleware('api')
                ->prefix('api')
                ->group(base_path('routes/api.php'));

            Route::middleware('web')
                ->group(base_path('routes/web.php'));
        });
    }

    /**
     * Configure the rate limiters for the application.
     *
     * @return void
     */
    protected function configureRateLimiting()
    {
        RateLimiter::for('api', function (Request $request) {
            return Limit::perMinute(60)->by($request->user()?->id ?: $request->ip());
        });
    }
}

RedirectAuthenticatedUsersController:

namespace App\Http\Controllers\Auth;

use App\Http\Controllers\Controller;
use Illuminate\Http\Request;

class RedirectAuthenticatedUsersController extends Controller
{
    public function home()
    {
        if (auth()->user()->role == 'admin') {
            return redirect('/adminDashboard');
        }
        elseif(auth()->user()->role == 'user'){
            return redirect('/userDashboard');
        }
        elseif(auth()->user()->role == 'guest'){
            return redirect('/guestDashboard');
        }
        else{
            return auth()->logout();
        }
    }
}
Sinnbeck's avatar

@AndrewL64 It looks correct to me. If you hover the register button, what url do you get in the lower left corner of the browser? If you click it, do you then get errors in the browser console?

AndrewL64's avatar

@sinnbeck

If you hover the register button, what url do you get in the lower left corner of the browser? If you click it, do you then get errors in the browser console?

Nothing shows up when I hover over both the login or register links. I guess it's because they are buttons and not anchor tags.

When I click on any of them, "Waiting for 127.0.0.1/login" or "Waiting for 127.0.0.1/register" quickly flashes in the bottom left but the page doesn't change other than show the progress bar loads for less than a second.

Monitoring the Network tab in Chrome's dev tools when clicking the links, I noticed that I get a 302 Status for the "other page" and 200 status for the "current page".

By other page, I mean register when I try clicking the links from the login page and login when I try clicking the links from the register page.

tldr: If I manually type and go to 127.0.0.1/login and then click on the Login link, I get 200 and when I click the Register link, I get 302. The status codes are reversed (200 for register | 302 for login) when I manually type and go to 127.0.0.1/register and click both the links.

AndrewL64's avatar
AndrewL64
OP
Best Answer
Level 1

Figured it out. The problem was because I was specifying the method for both login/register as POST requests instead of GET requests. Changing the method for both login and register to GET (as shown below) fixed the issue.

<Dropdown.Link href={route('login')} method="get" as="button">
  Log in
</Dropdown.Link>
<Dropdown.Link href={route('register')} method="get" as="button">
  Register
</Dropdown.Link>
1 like

Please or to participate in this conversation.