wonder95's avatar

Laravel 12 Breeze conflicts with path name case changes

I have been using Breeze with Vue and Inertia and Tailwind since Laravel 10, and it's been pretty flawless. However, the Laravel 12 installation has been somewhat painful, and one issue that stands out is casing of directory names vs values returned from controllers via Inertia.render().

One change has been the casing of the directory names under /resources/js. Where in the past, it was

resources/js/Pages/Auth

for example, it is now

resources/js/pages/auth

At the same time, the Vue component names are still upper case (e..g. login.vue). So, once I have everything installed and running, when I go to the Log In link, I get this console error

Uncaught (in promise) Error: Page not found: ./Pages/Auth/Login.vue

The resolve item in app.js

resolve: (name) => resolvePageComponent(`./Pages/${name}.vue`, import.meta.glob('./Pages/**/*.vue')),

can be changed to

resolve: (name) => resolvePageComponent(`./pages/${name}.vue`, import.meta.glob('./pages/**/*.vue')),

but that still doesn't handle the subdirectory (/Auth/) casing. On top of that, the return values in the RegisteredUserController.php and AuthenticatedSessionController.php still return the upper case values

    public function create(): Response
    {
        return Inertia::render('Auth/Register');
    }

and

    public function create(): Response
    {
        return Inertia::render('Auth/Login', [
            'canResetPassword' => Route::has('password.request'),
            'status' => session('status'),
        ]);
    }

Instead of changing those, I changed my resolve to this, and it works, at least for those two

    resolve: (name) => {
        // 1. Split the name into parts (e.g., "Auth/Login" -> ["Auth", "Login"])
        const parts = name.split('/');

        // 2. Lowercase all directory parts, but keep the last part (the file) PascalCase
        const transformedName = parts.map((part, index) => {
            return index === parts.length - 1 ? part : part.toLowerCase();
        }).join('/');

        // 3. Resolve using the corrected path
        return resolvePageComponent(
            `./pages/${transformedName}.vue`,
            import.meta.glob('./pages/**/*.vue')
        );
    },

However then I run into this again, in that when I navigate to the default Profile page, I get a similar error

Uncaught (in promise) Error: Page not found: ./pages/profile/Edit.vue

because the casing for /Profile/Partials is still upper case.

This seems like sort of a hot mess. Did I miss something, or are these things that need to be fixed?

0 likes
2 replies
LaryAI's avatar
Level 58

You're running into this issue because Laravel Breeze (as of v12) moved to using lowercase directory names for the Vue components (e.g., pages/auth/login.vue instead of Pages/Auth/Login.vue), but the conventions in controllers (e.g., Inertia::render('Auth/Login')) and sometimes the component names (PascalCase for files) haven't fully caught up, causing a mismatch.

Why does this happen?

  • Inertia looks for components by path. If controller returns 'Auth/Login' but your files are now at pages/auth/login.vue, the casing mismatch causes Inertia to not find the Vue files.
  • Your temporary resolve fix (lowercasing only directories) helps for the provided cases, but doesn't handle situations where there are subfolders with mixed case (like profile/Partials).

The Robust Solution

Approach:

  • Map all incoming names from controllers to the correct file paths (i.e., lowercase directories, correct file casing).
  • This is easiest if you enforce all folder and file names to be lowercase. If that's not an option, you'll need a dynamic approach.

Option 1: Change Your Project Structure

The simplest maintenance path—rename all directories and filenames to be lowercase.

  • Update references in both your JS (import.meta.glob) and PHP (controller) code to use lowercase everywhere.
  • e.g. Inertia::render('auth/login')

But if you can't or don't want to rename everything:


Option 2: Dynamic Page Resolver

Here's an improved resolver that will dynamically match files in your pages directory, regardless of case, so you don't have to worry about the mismatches.

// resources/js/app.js or wherever you set up Inertia/Vue

resolve: async (name) => {
    // Get ALL available pages (case-insensitive matching)
    const pages = import.meta.glob('./pages/**/*.vue');
    // Build a mapping of normalized (lowercase) paths to actual file importers
    const pageMap = {};
    for (const key in pages) {
        // Remove './pages/' and '.vue', lowercase, and replace backslashes (Windows)
        let normalized = key
            .replace(/^\.\/pages\//, '')
            .replace(/\.vue$/, '')
            .toLowerCase()
            .replace(/\\/g, '/');
        pageMap[normalized] = pages[key];
    }
    // Generate incoming name's lowercased path, replacing slashes
    const requested = name.toLowerCase();
    if (pageMap[requested]) {
        return pageMap[requested]().then((mod) => mod.default || mod);
    }
    throw new Error(`Page not found: ${name}`);
}

Replace your previous resolve function with this.

How it works:

  • When a controller says Inertia::render('Auth/Login'), this function will lowercase that to 'auth/login' and look for any .vue file matching in lowercase, regardless of how your folders or files are cased.

TL;DR

  • Best: Make all your page directories and filenames lowercase and match their use in controllers.
  • If you can't: Use the dynamic page resolver above to make file loading case-insensitive.

Why was this changed in Laravel Breeze 12? The update aligns with Tailwind, Vite, and other modern conventions (DRY, UNIX-style lowercasing for easier cross-platform dev). It also avoids issues on case-insensitive filesystems (like macOS and Windows). BUT it does require syncing your code's usage and paths.


References


In summary:

  • Either convert all folders, files, and controller references to lowercase,
  • OR use the "case-insensitive" dynamic resolver above.

Let me know if you need help with the actual renaming, or setting up the resolver!

Snapey's avatar

I don't use Inertia, but I don't believe Lary's response. There would not be this breaking change without it being mentioned in the upgrade guide?

Please or to participate in this conversation.