Today is the day, you can actually use React instead of VueJS with Laravel Jetstream, and I am going to show you how easy it is!

What you'll learn:

  1. Replace Vue with React
  2. Setup inertia-react
  3. Swap inertia-vue with React
  4. Create the register, login and dashboard page

Jetstream UI integration is not part of this tutorial, because most of the time you want to use a custom theme.


Setup Project

Let's start with a fresh Laravel + Jetstream:

laravel new jetstream-react --jet --stack=inertia -n

Now, open the project in your editor of choice.

Firstly, what you need to do is create a database and enter the correct credentials in your .env and run.

php artisan migrate

Secondly you should be able to open your project in the browser and use the default Laravel Jetstream Inertia installation. You could create an account, so it's easier to implement the login screen in React in a later phase.


Install React + Inertia/React

First, let's cleanup the required dependencies

npm remove @inertiajs/inertia-vue3 vue

and install React.

npm install react react-dom @inertiajs/inertia-react --dev

I chose to divide my packages in "infrastructure" as devDependencies and "styling/functional" in dependencies so it's easier to just use the devDependencies of a previous project to setup the new one without figuring everything out again.

Once the packages are installed we can setup our Laravel-Mix file.
Open your webpack.mix.js and replace the following code:

- mix.js('resources/js/app.js', 'public/js').vue()
+ mix.js('resources/js/app.js', 'public/js').react()

Now Laravel Mix knows we want to use React instead of VueJS, so we need to replace the inertia-vue entry point with the inertia-react one.


Replace Vue with React


Now it's time to create your first React component, so we can test if the React-setup works.

Create the following file: resources/js/Components/App.jsx and use the following code:

import React from "react"
import { render } from 'react-dom';
​
const el = document.getElementById('app');
​
const App = () => {
    return (<div>Laravel Jetstream with React</div>);
}
​
export default App
​
if (el) {
    render(<App />, el);
  
}

This App.jsx will be our Inertia entry point, we only need to load this component by opening your resources/js/app.js and replace all the code with the following:

import './bootstrap'
import { InertiaProgress } from '@inertiajs/progress';
import './Components/App';
​
InertiaProgress.init();

It's testing time! Run and visit your browser.

npm run dev

If everything worked out, you should see the text from our App.jsx.


Setup inertia-react

We have created a React entry point for our app, now we need to add the inertia-react component to actually load pages.

Open your App.jsx and replace our test text with the InertiaApp:

import { InertiaApp } from '@inertiajs/inertia-react';
​
return (<InertiaApp
    initialPage={JSON.parse(el.dataset.page)}
    resolveComponent={name => require(`./Pages/${name}`).default}
/>);

Create the folder resources/js/Components/Pages and run the Laravel Mix watch command.

npm run watch

Laravel Mix shows no errors, so that must be okay, but when you open the project in your browser and use DevTools to check the console, you see that Inertia tries to load the welcome page, but this does not yet exists in React, so let's create a welcome page.


Create the welcome page

Create the file resources/js/Components/Pages/Welcom.jsx with the following contents:

import React from 'react';
import { Link } from '@inertiajs/inertia-react';
​
const Welcome = ({canLogin, user, laravelVersion, phpVersion}) => {
    return (<div
        className="relative flex items-top justify-center min-h-screen bg-gray-100 dark:bg-gray-900 sm:items-center sm:pt-0">
        {canLogin &&
        <div className="hidden fixed top-0 right-0 px-6 py-4 sm:block">
            {user && <Link href={route('dashboard')
                           className="text-sm text-gray-700 underline">
                Dashboard
            </Link>}
​
            {!user && (<>
                <Link href={route('login')}
                      className="text-sm text-gray-700 underline">
                    Log in
                </Link>
                <Link href={route('register')}
                      className="ml-4 text-sm text-gray-700 underline">
                    Register
                </Link>
            </>)}
        </div>}
​
        <div className="max-w-6xl mx-auto sm:px-6 lg:px-8">
            <div
                className="ml-4 text-center text-sm text-gray-500 sm:text-right sm:ml-0">
                Laravel v{laravelVersion} (PHP v{phpVersion})
            </div>
        </div>
​
    </div>);
};
​
export default Welcome;
​

Et, Voila! You should see this welcome page when you refresh your browser. Now You've created the starting point for implementing all the Jetstream pages.


Vue > React

Well, here starts the actual challenge.
Replacing all the default Jetstream Vue pages and components with your own React versions of them. This sounds like a lot of work, but when you want to use your own theme within the Vue implementation, you still need to go over every component en build your own design. To get you started, we'll implement the login, register and dashboard pages. After this you should be albe to convert the rest of the components on your own!

Because of the different syntax between Vue and React it's not a wishful thought to use the same structure, for example, Vue slots don't look that pretty in React. This is something you need to keep in mind while converting the Vue pages to React.


Login

You should create a user in your database if you haven't already created one in the beginning of this post.

Create resources/js/Components/Pages/Auth/Login.jsx

import React from 'react';
import { useForm } from '@inertiajs/inertia-react';
​
const Login = () => {
    const {data, setData, post, processing, errors} = useForm({
        email: '',
        password: '',
        remember: false,
    });
​
    const handleChange = (e) => {
        const target = e.currentTarget;
        const value = target.type === 'checkbox'
            ? target.checked
            : target.value;
        setData(target.name, value);
    };
​
    const handleSubmit = (e) => {
        e.preventDefault();
        post(route('login'));
    };
​
    return (
        <form onSubmit={handleSubmit}>
            <div>
                <label>E-mail:</label>
                <input type="text" value={data.email} name="email"
                       onChange={handleChange}/>
                {errors.email && <div>{errors.email}</div>}
            </div>
            <div>
                <label>Password</label>
                <input type="password" value={data.password} name="password"
                       onChange={handleChange}/>
                {errors.password && <div>{errors.password}</div>}
            </div>
            <div>
                <input type="checkbox" checked={data.remember} name="remember"
                       onChange={handleChange}/> Remember Me
            </div>
            <div>
                <button type="submit" disabled={processing}>Login</button>
            </div>
        </form>
    );
};
​
export default Login;

Register

For the register page, create the file resources/js/Components/Pages/Auth/Register.jsx and add the following code:

import React from 'react';
import { useForm } from '@inertiajs/inertia-react';
​
const Register = ({jetstream}) => {
    const {data, setData, post, processing, errors, reset} = useForm({
        name: '',
        email: '',
        password: '',
        password_confirmation: '',
        terms: false,
    });
​
    const handleChange = (e) => {
        const target = e.currentTarget;
        const value = target.type === 'checkbox'
            ? target.checked
            : target.value;
        setData(target.name, value);
    };
​
    const handleSubmit = (e) => {
        e.preventDefault();
        post(route('register'), {
            onFinish: () => reset('password', 'password_confirmation'),
        });
    };
​
    return (
        <form onSubmit={handleSubmit}>
            <div>
                <label>Name:</label>
                <input type="text" value={data.name} name="name"
                       onChange={handleChange}/>
                {errors.name && <div>{errors.name}</div>}
            </div>
            <div>
                <label>E-mail:</label>
                <input type="text" value={data.email} name="email"
                       onChange={handleChange}/>
                {errors.email && <div>{errors.email}</div>}
            </div>
            <div>
                <label>Password</label>
                <input type="password" value={data.password} name="password"
                       onChange={handleChange}/>
                {errors.password && <div>{errors.password}</div>}
            </div>
​
            <div>
                <label>Conform password</label>
                <input type="password" value={data.password_confirmation}
                       name="password_confirmation"
                       onChange={handleChange}/>
                {errors.password_confirmation &&
                <div>{errors.password_confirmation}</div>}
            </div>
​
​
            {jetstream.hasTermsAndPrivacyPolicyFeature && (<div>
                <input type="checkbox" checked={data.terms} name="terms"
                       onChange={handleChange}/>
                I agree to the <a target="_blank" href={route('terms.show')}>Terms
                of Service</a> and <a target="_blank"
                                      href={route('policy.show')}>Privacy
                Policy</a>
            </div>)}
​
            <div>
                <button type="submit" disabled={processing}>Register</button>
            </div>
        </form>
    );
};
​
export default Register;

Dashboard

For the dashboard page, create the file resources/js/Components/Pages/Dashboard.jsx and add the following code:

import React from 'react';
import { Inertia } from '@inertiajs/inertia';
​
const Dashboard = ({user}) => {
​
    const handleLogout = (e) => {
        e.preventDefault();
        Inertia.post(route('logout'));
    };
    return <div>Welcome {user.name},
        <button onClick={handleLogout}>Log Out</button></div>;
};
​
export default Dashboard;

What's next?

You've created a good base to reconstruct the Vue pages into React pages. A Tip, first recreate the pages before you start with the Jetstream components. You can use the Vue components as a reference to the functionality per page. The process becomes easier because you only need to implement your own theme, the thinking is already done! There is still a long way to go before you completely implement all the Vue pages in React. Start with the pages you are actually going to use in you application followed up with the side features as two-factor, password reset et cetera.

I hope you now have a clear view on how to convert Inertia with Vue to React!

Geschreven door: Kevin Pijning

Meer kennis bijspijkeren? Kom dan naar onze Meetup: Ode aan de Code!

Bekijk onze Meetups

Wij zijn altijd op zoek naar getalenteerde vakgenoten!

Bekijk onze vacatures