With 9.19 version, Laravel offer a new bundle for assets, vite. If you use a Laravel version under 9.19, check this guide.

Node.js packages

pnpm i
pnpm remove axios lodash
pnpm add typescript -D

Views

<?php

namespace App\Providers;

use Illuminate\Support\Facades\View;
use Illuminate\Support\ServiceProvider;

class AppServiceProvider extends ServiceProvider
{
  /**
   * Register any application services.
   */
  public function register()
  {
      //
  }

  /**
   * Bootstrap any application services.
   */
  public function boot()
  {
    View::addNamespace('front', resource_path('front'));
  }
}
mkdir resources/front
mkdir resources/front/components
mkdir resources/front/layouts
mkdir resources/front/pages
touch resources/front/global.d.ts

mv resources/css resources/front/css

mv resources/js resources/ts
mv resources/ts resources/front/ts
mv resources/front/ts/app.js resources/front/ts/app.ts

rm resources/front/ts/bootstrap.js

tsconfig.json

touch tsconfig.json
{
  "compilerOptions": {
    "target": "esnext",
    "module": "esnext",
    "moduleResolution": "node",
    "strict": true,
    "jsx": "preserve",
    "sourceMap": true,
    "resolveJsonModule": true,
    "esModuleInterop": true,
    "skipLibCheck": true,
    "noImplicitAny": false,
    "lib": [
      "esnext",
      "dom"
    ],
    "types": [
      "vite/client"
    ],
    "typeRoots": [
      "./node_modules/@types",
      "resources/**/*.d.ts"
    ],
    "paths": {
      "~": [
        "./resources"
      ],
      "~/*": [
        "./resources/*"
      ]
    }
  },
  "include": [
    "resources/**/*.ts",
    "resources/**/*.d.ts",
    "resources/**/*.tsx",
    "resources/**/*.vue"
  ],
  "exclude": [
    "resources/views/stubs",
    "resources/webreader/scripts/library",
    "resources/admin/auto-imports.d.ts"
  ]
}

Vite

mv vite.config.js vite.config.ts
import { defineConfig } from 'vite'
import laravel from 'laravel-vite-plugin'

export default defineConfig({
  plugins: [
    laravel({
      input: ['resources/front/css/app.css', 'resources/front/ts/app.ts'],
      refresh: ['resources/**'],
    }),
  ],
})

Blade

mkdir resources/views/components
touch resources/views/components/app.blade.php
<!DOCTYPE html>
<html lang="{{ str_replace('_', '-', app()->getLocale()) }}">

<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible"
        content="IE=edge">
    <meta name="viewport"
        content="width=device-width, initial-scale=1.0">
    <meta name="csrf-token"
        content="{{ csrf_token() }}">

    <title>@yield('title', app_name())</title>
    {{-- {!! SEO::generate() !!} --}}

    <link rel="icon"
      type="image/x+ico"
      href="/favicon.ico">
    <link rel="apple-touch-icon"
      sizes="180x180"
      href="/apple-touch-icon.png">
    <link rel="icon"
      type="image/png"
      sizes="32x32"
      href="/favicon-32x32.png">
    <link rel="icon"
      type="image/png"
      sizes="16x16"
      href="/favicon-16x16.png">
    <link rel="manifest"
      href="/site.webmanifest">

    {{-- <script src="{{ asset('/color-mode.js') }}"></script> --}}

    @stack('head')
    @stack('styles')
    {{-- @livewireStyles --}}
</head>

<body
    class="{{ config('app.env') === 'local' ? 'debug-screens' : '' }} color-mode">
    {{ $slot }}

    @stack('modals')
    @stack('scripts')
    {{-- @livewireScripts --}}
</body>

</html>

Layout

touch resources/front/layouts/app.blade.php
<x-app>
  @push('head')
      @vite(['resources/front/css/app.css', 'resources/front/ts/app.ts'])
  @endpush
  @yield('default')
</x-app>

Page

touch resources/front/pages/index.blade.php
@extends('front::layouts.app')

@section('default')
  Content
@endsection

Controller

php artisan make:controller Front/FrontController
<?php

namespace App\Http\Controllers\Front;

use App\Http\Controllers\Controller;

class FrontController extends Controller
{
  public function index()
  {
    return view('front::pages.index');
  }
}

Routes

Route::get('/', [FrontController::class, 'index'])->name('front.index');

spatie/laravel-route-attributes

composer require spatie/laravel-route-attributes
php artisan vendor:publish --provider="Spatie\RouteAttributes\RouteAttributesServiceProvider" --tag="config"
<?php

return [
  // ...
  'directories' => [
    // app_path('Http/Controllers'),
  ],
  // ...
];
<?php

namespace App\Providers;

use Illuminate\Cache\RateLimiting\Limit;
use Illuminate\Foundation\Support\Providers\RouteServiceProvider as ServiceProvider;
use Illuminate\Http\Request;
use Illuminate\Routing\Router;
use Illuminate\Support\Facades\RateLimiter;
use Illuminate\Support\Facades\Route;
use Spatie\RouteAttributes\RouteRegistrar;

class RouteServiceProvider extends ServiceProvider
{
  public const HOME = '/home';

  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'))
        ;
    });

    Route::name('front.')
      ->group(
        fn () => (new RouteRegistrar(app(Router::class)))
          ->useRootNamespace(app()->getNamespace())
          ->useMiddleware(['web'])
          ->registerDirectory(app_path('Http/Controllers/Front'))
      )
    ;

    // Route::prefix('api')
    //   ->name('api.')
    //   ->group(
    //     fn () => (new RouteRegistrar(app(Router::class)))
    //       ->useRootNamespace(app()->getNamespace())
    //       ->useMiddleware(['api'])
    //       ->registerDirectory(app_path('Http/Controllers/Api'))
    //   )
    // ;
  }

  // ...
}
<?php

namespace App\Http\Controllers\Front;

use App\Http\Controllers\Controller;
use Spatie\RouteAttributes\Attributes\Get;

class FrontController extends Controller
{
  #[Get('/', name: 'front.index')]
  public function index()
  {
    return view('front::pages.index');
  }
}
<?php

- Route::get('/', [FrontController::class, 'index'])->name('front.index');