Creating a full-stack application using Inertia.js
Tags: webdev, react, laravel, inertiajs
Published: 2023-06-18 13:43:54

##### ✏️ Written by [Xander Van den Bossche](https://github.com/Xandervdb1)
For the past month I have been working on a full-stack application together with co-learners at a bootcamp, all the way from coming up with the idea for a product to deploying it. When it came to deciding which technologies we would use to bring this project to a good end, the front-end team was set on using on **React** for quick rendering of a single-page application, while back-end wanted to work with **Laravel** so they could benefit from an easy-to-use relational database. None of us had ever worked in a stack that combined the both, but after some asking around we found our solution: **Inertia.js**.

## 🤔 What is Inertia.js?
Inertia allows to combine both server-side rendering together with client-side rendering, with the use of routes and controllers which will render a single-page application using React pages and components. It isn't a framework like you might think, but rather acts like the **glue between your favorite server-side and client-side frameworks**, making them best friends.

![The office - Michael hugging Jim gif](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/8t51r9gperj4lr6i7w5e.gif)

As found on the [Inertia website](https://inertiajs.com/):
>Inertia is a new approach to building classic server-driven web apps. We call it the modern monolith.
>
>Inertia allows you to create fully client-side rendered, single-page apps, without the complexity that comes with modern SPAs. It does this by leveraging existing server-side patterns that you already love.
>
>Inertia has no client-side routing, nor does it require an API. Simply build controllers and page views like you've always done! Inertia works great with any backend framework, but it's fine-tuned for Laravel.

When we first read about this we thought we were in for a ride to learn about all the ins and outs of Inertia before we'd be able to use all of its benefits. I was in charge of researching the main differences from the regular frameworks, but was surprised by how easy-to-use and convenient it actually is to use Inertia. In this article I'll talk about how to set up a **Laravel - React app using the modern monolith**, and the important differences you'll need to know to become an Inertia pro.

## 🚀 Getting started with Inertia.js
### Before you dive in
You'll need to make sure you have following:
- [Node.js 10x](https://nodejs.org/en/download) or higher & [npm 5.2](https://docs.npmjs.com/getting-started) or higher
- [PHP](https://www.php.net/manual/en/install.php) 7.2.5 or higher, [Composer](https://getcomposer.org/) and [Laravel](https://laravel.com/docs/6.x)
- A basic knowledge of Laravel & React

### Lift off!

1. Creating a Laravel project in the current directory
    ```
    composer create-project laravel/laravel .
    ```
    &nbsp;
2. Add breeze (Laravel starterkit), to install React with it later
    ```
    composer require laravel/breeze --dev
    ```
    &nbsp;
3. Have breeze add React to your project (using Inertia.js)
    ```
    php artisan breeze:install react
    ```
    &nbsp;
4. Install and run vite & tailwind
    ``` 
    npm i
    npm run dev
    ```
    &nbsp;
5. Set up your database settings in .env
    ```
    DB_CONNECTION=mysql
    DB_HOST={database host}
    DB_PORT={database port}
    DB_DATABASE={database name}
    DB_USERNAME={database username}
    DB_PASSWORD={database password}
    ```
    &nbsp;
6. Migrate the database & start up the server
    ```
    php artisan migrate
    php artisan serve
    ```
    &nbsp;
7. Pat yourself on the back! 🎊

![The office - dance party gif](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/0623qms37y7fe3wzizkz.gif)

## How to use Inertia to the fullest
Since Inertia will help you combine your favorite frameworks together, there'll be slight changes to how you would use them normally. I have listed most major changes below, but encourage going through the [documentation](https://inertiajs.com/) if you'd like the full list, since all of them are described there.
### 📄 Pages
Just like views in Laravel, you'll have Pages (that you'll render with requests) which are located in the `resource/js/Pages/` directory as a .jsx file

An example of what a basic page would look like:
```jsx
//Index.jsx
const Index = () => {
    return (
        <>
            <h1>Hello World - Index page</h1>
        </>
    );
}
export default Index
```

![Big bang theory - throwing papers around](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/qab8pqwjbpzjffs5ejzc.gif)

#### Rendering a page
A page will be rendered by a route inside `routes/web.php`. There's two ways to render a page. Both ways, Inertia will go search for the page in the Pages directory by default, and look for `{name}.jsx` (just like using view() in Laravel).
```jsx
//web.php
//using the inertia() helper function
Route::get('/', function () {
    return inertia('Index');
    //resources/js/Pages/Index.jsx
});

//or

//using the Inertia object and render method
use Inertia\Inertia;

Route::get('/', function () {
    return Inertia::render('Index');
    //resources/js/Pages/Index.jsx
});
```
#### Passing variables to a page
It is possible to pass variables through to the Page in the same way as you can in Laravel.
```jsx
//web.php
Route::get('/', function () {
    return Inertia::render('Index', [
        'name' => 'Xander'
    ]);
});
```
The `name` variable can then be accessed inside the Page file using props as a parameter passed through inside the function, just like in React.
```jsx
//Index.jsx
const Index = (props) => {
    return (
        <>
            <h1>Hello World - {props.name}</h1>
        </>
    );
}
export default Index
```
### ⚓ Links - forget about anchor tags
Anchor tags will reload the page, which will defeat the purpose of making a single-page app using React!

![The office creed stating an url gif](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/w1qo6zz77eaq9g719032.gif)

#### The Link component
Inertia will handle anchor tags differently in order to not break the React flow, by using the Link component, which you can import in your Pages or Components wherever needed.
```jsx
import { Link } from "@inertiajs/react";
```
Now you can replace all `<a>` tags with `<Link>` tags, preventing a page from being reloaded when a visitor clicks on a link.
```html
<nav>
    <ul>
        <li><Link href="/">Home</Link></li>
        <li><Link href="/about">About</Link></li>
        <li><Link href="/contact">Contact</Link></li>
    </ul>
</nav>
```

### 🎨 Layouts
If you have code that will be used on most pages, for example the code for the navigation at the top of the pages, you can create a `<Layout/>` component. While it will be treated as a normal React component, you'll be able to nest the children of the `<Layout/>` component in any place, and keep your code DRY. This way, you won't have to edit the same code on multiple pages when you want to add a new link to the navigation.

The `children` parameter passed through to the Layout component will contain any tags and/or components that the `<Layout>` tag would wrap.

An example of a Layout component:
```jsx
//Layout.jsx
const Layout = ({children}) => {
    return (
        <>
            <div>
                This is a layout component used on every single page
            </div>
            <div>
                We can choose to show the tags nested inside the
                Layout component tags at any place
            </div>
            <div>
                Here for example:
            </div>
            {children}
            <div>
                This is the end of the layout component
            </div>
        </>
    )
}
export default Layout;
```
```jsx
//Index.jsx
import Layout from './Layout';
import Nav from './Components/Nav';
const Index = () => {
    return (
        <>
            <Layout>
                <Nav />
                <p>Extra content</p>
            </Layout>
            <h1>Hello World - Welcome page</h1>
        </>
    );
}
export default Index;
```
The resulting code for the Index page would be:
```html
<div>
    This is a layout component used on every single page
</div>
<div>
    We can choose to show the tags nested inside the
    Layout component tags at any place
</div>
<div>
    Here for example:
</div>
<!-- start children variable -->
<nav>
    <ul>
        <li><Link href="/">Home</Link></li>
        <li><Link href="/about">About</Link></li>
        <li><Link href="/contact">Contact</Link></li>
    </ul>
</nav>
<p>Extra content</p>
<!-- end children variable -->
<div>
    This is the end of the layout component
</div>
<h1>Hello World - Welcome page</h1>
```
#### Persistent layouts
Using the layout as a component works for most cases, but there are a few things to keep in mind while doing so. Since the Layout component is a child of the Page component, every time you render a different page, the Layout component will be destroyed then rebuilt each time. 

If you want to prevent this (for several reasons, like performance, or there is a video inside the layout that shouldn't be restarted on each render of another page) you can make use of persistent layouts.

```jsx
import Layout from './Layout'

const Index = (props) => {
  return (
    <>
      <H1>Welcome</H1>
      <p>Hello {props.user.name}, 
      welcome to your first Inertia app!</p>
    </>
  )
}

Index.layout = page => <Layout children={page} title="Welcome" />

export default Index
```

### ✉️ Non-GET requests with Links

If, for example, you want to log a user out, you'll have to do this through a POST request. In Inertia, you are able to make a Link use the POST method instead of the default GET request. When doing this, you'll also have to specify that the Link component will now be used as a button, using the `as` attribute.

```html
<Link method="POST" as="button" href="/logout">Log out</Link>
```
```jsx
//web.php
Route::post("/logout", function() {
    dd("handle logout");
});
```
![Dog going through mail gif](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/p8fcypw3xjj9mhari746.gif)

#### Passing through variables
You'll be able to pass data to the POST request, and receive them the same way as if the POST was made with a form. For this, you can use the `data` attribute, which will accept an object.

```jsx
<Link 
    method="POST" 
    data={{foo: "bar"}} 
    as="button" 
    href="/logout"> Log out </Link>
```
```jsx
//web.php
Route::post("/logout", function() {
    dd(request('foo')); //"bar"
});
```
### 🕵️ Share data across pages & components
If you want data to be accessible on every page/component (for example, the data for a logged in user), you can use the share function inside the Inertia middleware, located in `app/Http/Middleware/HandleInertiaRequest.php`.

```jsx
//HandleInertiaRequest.php
public function share(Request $request): array
    {
        return array_merge(parent::share($request), [
            'auth' => [
                'user' => $request->user() ? $request->user()->only(
                    'id',
                    'name',
                    'email',
                    'birthday',
                    'username',
                ) : null,
            ],
        ]);
    }
```
To access the shared data, you can use the `usePage()` hook from Inertia.
```jsx
//Layout.jsx
import { usePage } from '@inertiajs/react'

export default function Layout({ children }) {
  const { auth } = usePage().props

  return (
    <main>
      <header>
        You are logged in as: {auth.user.name}
      </header>
      <content>
        {children}
      </content>
    </main>
  )
}
```
When making data globally available throughout the application, it's a good idea to namespace as detailed as possible, to prevent ending up with variables that are named the same. As you can see to access the logged in user with a namespaced variable, you'll have to use `auth.user.name`, while a `user` variable (that doesn't have the logged in user assigned) may still be used elsewhere on the page.

⚠️ Never share sensitive information this way. Since this global data will be available client-side, anyone could access all of this data, even when you don't render it on a page.

![Secret information gif](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/3e5tsle5zgz2jqrhbxg7.gif)

### 🗣️ Head & Title tags
If you want to change the meta tags or titles of your pages, you'll have to use the `<Head>` component, which Inertia offers us.

```jsx
import { Head } from '@inertiajs/react'

<Head>
  <title>Your page title</title>
  <meta name="description" content="Your page description" />
</Head>
```

📃 Changing the title will still add "- Laravel" behind it. This is the app name, defined in the `.env` file, which you should change to your own.

If you only need to add a title, you can use the shorthand.
```jsx
import { Head } from '@inertiajs/react'

<Head title="Your page title" />
```

#### What if you have multiple Head instances?
Inertia will overwrite the title tag to the one specified inside your specific page component. For meta-tags, we'll need to tell it which ones are the same and should be overwritten by the ones specified inside the page component, by passing along the `head-key` attribute.

```jsx
// Layout.js
import { Head } from '@inertiajs/react'

<Head>
  <title>My app</title>
  <meta 
    head-key="description" 
    name="description" 
    content="This is the default description" 
   />
  <link 
    rel="icon" 
    type="image/svg+xml" 
    href="/favicon.svg" 
  />
</Head>
```
```jsx
// About.js
import { Head } from '@inertiajs/react'

<Head>
  <title>About - My app</title>
  <meta 
    head-key="description" 
    name="description" 
    content="This is a page specific description" 
  />
</Head>
```
```html
<!-- RESULT -->
<head>
  <link rel="icon" type="image/svg+xml" href="/favicon.svg" />
  <title>About - My app</title>
  <meta 
    name="description" 
    content="This is a page specific description" 
  />
</head>
```
### 🏝️ Some quality of life features
Inertia offers a few features built-in to help you save time instead of having to write obnoxious/long code.

![Dog on the beach vacation gif](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/owq0t7hk1jdl62ap988k.gif)
#### Progress bar when loading pages
By default, there is a loading bar at the top of the page whenever the user loads a different page or makes a request.
If you want to disable this loading bar, we can modify the `progress` property inside the `createInertiaApp` function, which you can find at `resources/js/app.jsx`
```jsx
createInertiaApp({
  progress: false,
  // ...
})
```
There are several options to customize the look of the progress bar.
```jsx
createInertiaApp({
  progress: {
    /* The delay after which the progress bar will appear, 
    in milliseconds... */
    delay: 250,

    // The color of the progress bar...
    color: '#29d',

    // Whether to include the default NProgress styles...
    includeCSS: true,

    // Whether the NProgress spinner will be shown...
    showSpinner: false,
  },
  // ...
})
```
#### Preventing a page from scrolling up when a request is made
Whenever a link is clicked, and it redirects to the same page, the page will be scrolled back to the top. In Inertia, you can prevent this from happening, by using the `preserveScroll` attribute inside a `<Link/>` component
```jsx
<Link method="POST"
      as="button"
      data={{post-id: 1, user-id: 5}}  
      preserveScroll>
Like the post
</Link>
```
#### Styling active links
When the user visits a page, you have the ability to show the user what page they're currently on inside the nav, using conditional classes.

Inertia can check which Page component is currently rendered, and style the link based on that. In order to use this information, you can import the `usePage()` hook.

```jsx
import { usePage } from '@inertiajs/react'
const { url, component } = usePage()

//inside the navigation
<Link href="/users" className={component.startsWith('Users') ? 
    'active' :
    ''}>
    Users
</Link>
```
## 😎 That's all folks
And that's it, you're now ready to become an Inertia pro! I hope these points will help and make it easier for you to write your first Laravel - React application using Inertia, as much as they helped me and my co-learners. I definitely also recommend the [Laracast videos on Inertia](https://laracasts.com/series/build-modern-laravel-apps-using-inertia-js), as well as the [documentation page](https://inertiajs.com/) itself, for more information.

![That's all folks gif](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/afvl7r1ybk0lm8l62dcn.gif)