Looking to hire Laravel developers? Try LaraJobs

laravel-modules-inertia maintained by romkaltu

Description
Inertia.js page resolution for nwidart/laravel-modules
Last update
2026/04/06 14:57 (dev-master)
License
Links
Downloads
133

Comments
comments powered by Disqus

Laravel Modules Inertia

If you're using nwidart/laravel-modules with Inertia.js, you've probably run into the same problem we did: Inertia has no idea your modules exist. Page resolution breaks, Vite doesn't know where your module assets live, and you end up writing a bunch of glue code to make it all work.

This package fixes that. It gives you automatic page resolution for module views, a Vite plugin that sets up import aliases for your modules, and an artisan command to keep your tsconfig.json and Tailwind config in sync.

Requirements

Installation

composer require romkaltu/laravel-modules-inertia

Then add the package to your package.json dependencies so your frontend tooling can import from it:

{
    "devDependencies": {
        "laravel-modules-inertia": "file:vendor/romkaltu/laravel-modules-inertia"
    }
}

Run npm install (or pnpm install) to symlink it into node_modules/. After that, imports like 'laravel-modules-inertia/vite' just work.

The service provider is auto-discovered, so you don't need to register it manually.

Publish the config (optional)

php artisan vendor:publish --tag=inertia-modules-config

This publishes config/inertia-modules.php where you can customize the modules path. By default, it reads from your laravel-modules config or falls back to base_path('Modules').

How It Works

The Problem

With a standard Inertia setup, when you call Inertia::render('trucks/index'), Laravel looks for pages in resources/js/pages/. But in a modular app, you want your Fleet module's pages to live in Modules/Fleet/resources/views/, not mixed into the main app.

The Solution

This package hooks into Inertia's view finder so that page names like Fleet/trucks/index automatically resolve to Modules/Fleet/resources/views/trucks/index.tsx. The first segment before the slash is treated as the module name, and the rest is the page path within that module.

If no matching file is found in the module, it falls back to the default Inertia page resolution — so your existing app pages keep working as before.

Setup

1. Vite Plugin

Add the Vite plugin to your vite.config.ts:

import { defineConfig } from 'vite';
import laravel from 'laravel-vite-plugin';
import { inertiaModules } from 'laravel-modules-inertia/vite';

export default defineConfig({
    plugins: [
        laravel({
            input: 'resources/js/app.tsx',
            refresh: true,
        }),
        inertiaModules(),
    ],
});

The plugin scans your Modules/ directory and creates Vite aliases for each module that has a resources/js/ folder:

  • Modules/Fleet/resources/js/@fleet
  • Modules/Auth/resources/js/@auth

This means you can import from modules like this:

import { TruckForm } from '@fleet/components/TruckForm';

Conflict Detection

If a module name conflicts with an npm package (e.g., you have a module called React), the plugin automatically appends -module to the alias (@react-module) and warns you about it. You can also set explicit aliases:

inertiaModules({
    aliases: {
        Fleet: '@my-fleet',
    },
});

Plugin Options

Option Default Description
modulesDir 'Modules' Path to modules directory, relative to project root
prefix '@' Prefix for auto-generated aliases
conflictSuffix '-module' Suffix appended when an alias conflicts with an npm package
conflicts auto-detected Module names (lowercase) that should get the conflict suffix
aliases {} Explicit alias overrides, e.g. { Fleet: '@my-fleet' }

2. Page Resolver

Set up the Inertia page resolver to handle module pages. You can either use the publishable stub or wire it up manually.

Using the stub

php artisan vendor:publish --tag=inertia-modules-stubs

This creates resources/js/inertia/config.tsx with a ready-to-use setup.

Manual setup

import { createPageResolver } from 'laravel-modules-inertia/resolve-page';
import type { ResolvedComponent } from '@inertiajs/react';

const applicationPages = import.meta.glob<ResolvedComponent>('../pages/**/*.tsx');
const modulePages = import.meta.glob<ResolvedComponent>('../../../Modules/*/resources/views/**/*.tsx');

export const resolveInertiaPage = createPageResolver({
    applicationPages,
    modulePages,
});

Then use it in your app.tsx:

import { resolveInertiaPage } from './inertia/config';

createInertiaApp({
    resolve: resolveInertiaPage,
    // ...
});

Resolver Options

Option Default Description
applicationPages required Glob result for app pages (resources/js/pages/)
modulePages required Glob result for module pages (Modules/*/resources/views/)
modulePagePrefix '../../../Modules/' Prefix in glob keys for module pages
appPagePrefix '../pages/' Prefix in glob keys for app pages
extension '.tsx' File extension for pages

3. Sync TypeScript & Tailwind Config

After creating or removing modules, run:

php artisan inertia-modules:sync

This outputs the configuration you need to add to your tsconfig.json and CSS file:

TypeScript paths — so your editor understands the @module import aliases:

{
    "compilerOptions": {
        "paths": {
            "@modules/*": ["./Modules/*"],
            "@fleet/*": ["./Modules/Fleet/resources/js/*"]
        }
    },
    "include": [
        "Modules/*/resources/views/**/*.ts",
        "Modules/*/resources/views/**/*.tsx",
        "Modules/*/resources/js/**/*.ts",
        "Modules/*/resources/js/**/*.tsx"
    ]
}

Tailwind sources — so Tailwind scans your module templates:

@source '../../Modules/*/resources/views';
@source '../../Modules/*/resources/js';

Auto-write mode

To patch tsconfig.json directly instead of copying manually:

php artisan inertia-modules:sync --write

This merges the paths and includes into your existing tsconfig.json without touching your other settings. Safe to run multiple times — it won't create duplicate entries.

Expected Module Structure

The package expects each module to follow this layout:

Modules/
  Fleet/
    resources/
      views/          ← Inertia pages (resolved by the page resolver)
        trucks/
          index.tsx
          show.tsx
      js/             ← Shared JS/TS code (aliased by the Vite plugin)
        components/
          TruckForm.tsx
        hooks/
          useTrucks.ts
  • resources/views/ — Inertia page components. These are what you pass to Inertia::render('Fleet/trucks/index').
  • resources/js/ — Shared module code (components, hooks, utilities). Accessible via the @fleet alias in imports.

Usage in Controllers

Once set up, rendering module pages from controllers works exactly like you'd expect:

// In Modules/Fleet/Http/Controllers/TruckController.php

use Inertia\Inertia;

class TruckController extends Controller
{
    public function index()
    {
        return Inertia::render('Fleet/trucks/index', [
            'trucks' => Truck::all(),
        ]);
    }
}

Pages without a module prefix (e.g., Inertia::render('Dashboard')) continue to resolve from the standard resources/js/pages/ directory.

Other Frameworks

This initial release ships with first-class React support. Vue and Svelte adapters are on the roadmap — the page resolver is framework-agnostic under the hood, so adding support for other Inertia adapters is mostly a matter of providing the right stubs and documenting the glob patterns. Stay tuned.

Configuration

config/inertia-modules.php

return [
    /*
     * The absolute path to the modules directory.
     * When null, it's resolved from nwidart/laravel-modules config
     * (config('modules.paths.modules')), falling back to base_path('Modules').
     */
    'modules_path' => null,
];

Testing

# PHP tests
composer test

# JavaScript tests
npm test

# Static analysis
composer analyse

# Code formatting
composer format

License

MIT. See LICENSE for details.