Laravel Jetstream with Inertia and React
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:
- Replace Vue with React
- Setup inertia-react
- Swap inertia-vue with React
- 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';
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!