Skip to main content
Version: 1.0

Quick Start Installation

This tutorial assumes that you already have a Laravel 10 (or later) project up and running with PHP 8.2, and that you use the vite bundler. These instructions are based on the examples provided in the laravext repository.

You can also check the starter kits available at the laravext repository, in the starter-kits directory.

Composer

First, install the composer package:

composer require arthurydalgo/laravext

you can also publish the config file to make changes such as default root view, nexus/strands directory, server side rendering, etc:

php artisan vendor:publish --tag=laravext-config

By default, the root_view is set as sections.app, which means you should either have that view file, or change the config to fit your needs.

The next step can be done at the end of your ./routes/web.php file, or in a separated file, such as a ./routes/laravext.php, for example, and then you can require it at the end of your ./routes/web.php file. This is recommended, and is explained a little further in the Tools/Routing/Route Priority section of this documentation.

// ./routes/web.php

// Any other routes you might have

require __DIR__.'/laravext.php';
// ./routes/laravext.php

use Illuminate\Support\Facades\Route;

Route::laravext();

This technically optional, as there're other ways to generate your routes in a more granular way (which you can check at Tools/Routing), but it's entirely up to you on how you want to use it.

NPM

Install the npm modules:

Note: the additional instructions to use Typescript are at the end of this section
npm install @laravext/react

# Additionally, you should have the following package installed, if you haven't already
npm install @vitejs/plugin-react laravel-vite-plugin

# If you're planning to use typescript, you should also install the following packages
npm install --save-dev typescript @types/react @types/react-dom

or

npm install @laravext/vue3

# Additionally, you should have the following package installed, if you haven't already
npm install @vitejs/plugin-vue laravel-vite-plugin

# If you're planning to use typescript, you should also install the following packages
npm install --save-dev typescript vue-tsc

This example also assumes that you have a bootstrap.(js|ts) at ./resources/js and an app.css in you ./resources/css directory. You might or not have any need for those.

Now, you'll need to create an app.(js|jsx|ts|tsx) in your ./resources/js directory. The example below makes use of some npm packages such as i18n, moment, etc. You can remove them and the setup methods if you're not going to use them.

You'll then need to declare a createLaravextApp function, and pass an object with (at least) the following properties (assuming you'll make use of the @nexus and @strand blade directives):

  • nexusResolver: A function that resolves the nexus components. You can use the resolveComponent function from the @laravext/react/tools or @laravext/vue3/tools package to do so.
  • strandsResolver: A function that resolves the strand components. You can use the resolveComponent function mentioned above to do so.

There're some optional attributes included in the examples below that either commented or have comments explaining what they do. You can use them to set up global variables, internationalization, cookies, etc.

app.jsx:

import './bootstrap';
import '../css/app.css';
import i18n from "i18next";
import Cookies from "js-cookie";
import pt from './../../lang/pt.json'
import mdxeditor from './../../lang/pt/mdxeditor.json'
import { initReactI18next } from "react-i18next";
import { createLaravextApp } from "@laravext/react"
import { resolveComponent } from "@laravext/react/tools"
import moment from 'moment/min/moment-with-locales';

document.addEventListener('DOMContentLoaded', function () {
createLaravextApp({
nexusResolver: (name) => resolveComponent(`./nexus/${name}`, import.meta.glob('./nexus/**/*')),
strandsResolver: (name) => resolveComponent(`./strands/${name}.jsx`, import.meta.glob('./strands/**/*.jsx')),

// The beforeSetup function is executed once, before any of the setups.
// You can use this to set something up, such as internationalization.
beforeSetup: ({ laravext }) => {
const user = laravext.page_data.shared_props?.auth?.user;

// This is just for example purposes, using i18n/moment is not a requirement
i18n
.use(initReactI18next)
.init({
resources: {
pt: {
translation: { ...pt, ...mdxeditor }
}
},
fallbackLng: "en",
interpolation: {
escapeValue: false
}
});

let locale = user?.locale ?? Cookies.get('locale') ?? 'en';

i18n.changeLanguage(locale);
moment.locale(locale);
},

// Like Inertia, there's a wrapper for the https://ricostacruz.com/nprogress library.
// You don't have to declare this, while using the createLaravextApp, but so
// you know, these are the default values:
progress: {
delay: 0, // How many miliseconds until the loading bar appears
color: '#29d', // The color of said bar
includeCSS: true, // Wether or not to use NProgress' default styling
showSpinner: false, // Wether or not to show the spinner
},

// or, if you don't want it at all:
// progress: false,

// This setup is applied to all components, including nexus and strands
// setup: ({ component, laravext }) => {
// return <AnyComponentOrProvider>
// {component}
// </AnyComponentOrProvider>
// },

/// The setupNexus function is applied only to the nexus component, after the 'setup'
// function, unless reverseSetupOrder is true
// setupNexus: ({ nexus, laravext }) => {
// return <AnyComponentOrProvider>{nexus}</AnyComponentOrProvider>;
// },

// The setupStrand function is applied only to the strand components, after the 'setup' function,
// unless reverseSetupOrder is true.
// The 'strandData' parameter is the data passed to the strand component from the blade
// where it was located, if applicable.
// setupStrand: ({strand, laravext, strandData}) => {
// return <AnyComponentOrProvider>{strand}</AnyComponentOrProvider>
// },

// If you want to reverse the order of the setup functions, set this to true
// reverseSetupOrder: true,

// If for some reason you to change the order of the file conventions, you can do so here.
// It'll be applied to the nexus components, from a first to last basis, encapsulating
// the page component. Check the File Conventions section for more details.
// conventions: [
// 'error',
// 'layout',
// 'middleware',
// ],

// If for some reason you must disable the pushState of the laravext client router, you can do
// so here. Be aware that this will disable the back navigation, and you're user will endup
// going back to the previous non-laravext page.
// disablePushedStateData: () => {
// // Your logic here, such as detecting browsers, etc.
// let logicResult = true
// return logicResult
// },

// By default, the popstate event registered by laravext will ignore the event if the state is null.
// If you want to change this behavior, you can do so here.
// ignorePopStateEvent: (event) => {
// let logicResult = event.state === null;
// return logicResult;
// },
})
}, false);

You can change the nexus and strands' locations if you want to. Make sure to change the nexus directory in the ./config/laravext.php file. For more details on how the router works, check the router section.

Vite Configuration

Make sure you have a vite.config.js that looks something like this (assuming you've already installed either @vitejs/plugin-react or @vitejs/plugin-vue).

This example assumes you've defined a VITE_APP_ENV and VITE_APP_DOMAIN in your .env file, which contain the APP_ENV and APP_DOMAIN values, respectively.

Change any configuration to fit your needs.

import { defineConfig, loadEnv } from 'vite';
import laravel, { refreshPaths } from "laravel-vite-plugin";

// For React
import react from "@vitejs/plugin-react";

// For Vue
import vue from '@vitejs/plugin-vue';

export default function ({ mode }) {
const env = loadEnv(mode, process.cwd());

const host = env.VITE_APP_ENV == "local" ? env.VITE_APP_DOMAIN : undefined;

return defineConfig({
server: { host },
plugins: [
laravel({
input: [
'resources/css/app.css',
'resources/js/app.(js|jsx|ts|tsx)', // change the extension to fit your needs
],
refresh: [...refreshPaths, "resources/js/**", "app/**"],
}),

// For React
react(),

// For Vue
vue({
template: {
transformAssetUrls: {
base: null,
includeAbsolute: false,
},
},
}),
],
})
};

Blade

Assuming you have a ./resources/views/layouts/app.blade.php file, where a section is inserted, you'll need to insert some blade directives in it.

(The following code contains a $head variable that is described in the Tools/Nexus Response/withHead section. You can remove it if you're not going to use it)

<!DOCTYPE html>
<html lang="{{ str_replace('_', '-', app()->getLocale()) }}">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<meta name="csrf-token" content="{{ csrf_token() }}">

<!-- Check the Tools/Nexus Response/withHeadTitle($title) and withHeadDescription($description) section in the docs for more details -->
<title>{{ @$head['title'] ?? config('app.name', 'Laravel') }}</title>
<meta name="description" content="{{ @$head['description'] ?? config('app.description') }}">

<!-- Fonts -->
<link rel="preconnect" href="https://fonts.bunny.net">
<link href="https://fonts.bunny.net/css?family=figtree:400,500,600&display=swap" rel="stylesheet" />

<!-- Style -->
@vite(['resources/css/app.css'])
</head>
<body class="font-sans text-gray-900 antialiased">
<div class="min-h-screen flex flex-col sm:justify-center items-center pt-6 sm:pt-0 bg-gray-100 dark:bg-gray-900">
<div class="w-full sm:max-w-[95%] mt-6 px-6 py-4 bg-white dark:bg-gray-800 shadow-md overflow-hidden sm:rounded-lg text-white">
@yield('content')
</div>
</div>
@laravextScripts <!-- This will create a __laravext context variable -->
@viteReactRefresh <!-- In case you're using React -->

@vite(['resources/js/app.jsx']) <!-- In case you're using React -->
@vite(['resources/js/app.js']) <!-- In case you're using Vue -->
</body>
</html>

then, assuming you have a ./resources/views/sections/app.blade.php (or any other view that you're going to render a nexus):

@extends('layouts.app')
@section('content')
@nexus
@endsection

Now if you run

npm run dev

Everything should be up and running.

Nexus Directory

By default, the nexus directory is set as ./resources/js/nexus. So this is where you should start placing the files that will be used by Laravext's Router. You may change this in the ./config/laravext.php file, assuming you've published the config file.

You're now ready to start creating your project using the laravext router

Typescript

If you want to use Typescript, you might need to add some files to your project, and also install the NPM packages mentioned by the # If you're planning to use typescript [...] comments above in the NPM section.

First, you'll need a tsconfig.json file in your project root directory. Here's an example (your needs may vary, so feel free to change it):

tsconfig.json:

{
"compilerOptions": {
"allowJs": true,
"module": "ESNext",
"moduleResolution": "bundler",
"jsx": "react-jsx",
"strict": true,
"isolatedModules": true,
"target": "ESNext",
"esModuleInterop": true,
"forceConsistentCasingInFileNames": true,
"noEmit": true,
"paths": {
"@/*": ["./resources/js/*"],
}
},
"include": ["resources/js/**/*.ts", "resources/js/**/*.tsx", "resources/js/**/*.d.ts"]
}

Now you'll need to define your types. Create a @types directory in your ./resources/js directory, and create a laravext.d.ts file in it. Here's what needs to be inside of it:

laravext.d.ts:

declare module "@laravext/react" {
export const createLaravextApp: any;
export const createLaravextSsrApp: any;
export const Head: any;
export const Link: any;
export const laravextPageData: any;
export const version: any;
export const nexus: any;
export const nexusProps: any;
export const sharedProps: any;
export const routeParams: any;
export const routeName: any;
export const queryParams: any;
export const visit: any;
}

declare module "@laravext/react/server" {
export const serve: any;
}

declare module "@laravext/react/tools" {
export const resolveComponent: any;
}

declare module "@laravext/react/progress" {
export const injectCSS: any;
export const setupProgress: any;
export const startProgress: any;
export const moveProgress: any;
export const endProgress: any;
}

If you haven't already, you might need to declare a ./resouces/js/@types/vite-env.d.ts file, with the following content:

/// <reference types="vite/client" />

and you might also need a ./resources/js/@types/global.d.ts file, to set up stuff you might be using, like Axios, for example:

import { AxiosInstance } from 'axios';

declare global {
interface Window {
axios: AxiosInstance;
}
}

and maybe a ./resources/js/@types/index.d.ts file, to export all the types you might have:

export interface User {
id: number;
name: string;
email: string;
email_verified_at?: string;
}

export type sharedProps<T extends Record<string, unknown> = Record<string, unknown>> = T & {
auth: {
user: User;
};
motivation: string;
};

Needless to say, some of the examples from the NPM section above might need to be changed to fit your needs, like declaring the type of the name parameter in the nexusResolver and strandsResolver functions, for example:

import './bootstrap';
import '../css/app.css';

// For React
import { createLaravextApp } from "@laravext/react"
import { resolveComponent } from "@laravext/react/tools"

// For Vue
import { createLaravextApp } from "@laravext/vue3"
import { resolveComponent } from "@laravext/vue3/tools"

createLaravextApp({
// Added the 'string' type here
nexusResolver: (name: string) => resolveComponent(`./nexus/${name}`, import.meta.glob('./nexus/**/*')),

// The rest of the code, which might also need some changes
});

Looking for server side rendering?

Our princess is in another castle!

Credits: By Nintendo - https://www.mariowiki.com/File:Mushroom_Retainer_SMB1_W1-4_rescued.png, Fair use, Link (if any of Nintendo's lawyers end up here, please just let me know that I'll remove this image, no need to sue me)

Check the Server Side Rendering section for more details on how to use it.