Combining Adonis and Svelte using InertiaJS

Suggest An Edit

Table of Contents

Introduction

These days web apps are usually built with a separate backend and frontend. They communicate through REST API, GraphQL, etc. This is because a SPA framework/library like React, Svelte, or Vue provides a much better DX and UX compared to the old school templating languages like EJS, HBS, Blade, etc.

Now, what if, we combine the old school monolithic approach with those SPA frameworks/libraries? Thankfully, there’s InertiaJS!

In this post, I’ll try to explain how to get it up and running using Adonis and Svelte, but I won’t use Typescript for Svelte to keep it simple.

What is InertiaJS

Quoting from its website:

Inertia is a new approach to building classic server-driven web apps. We call it the modern monolith.

Basically, it lets you use a SPA framework/library as the template language for your backend framework of choice, whether it be Laravel, Ruby on Rails, Adonis, and many more.

You should check their website! It has a thorough explanation of what it does, who is it for, how it works, etc.

I’ll assume you already know Adonis/Svelte since the new hotness here is InertiaJS.

Installation

Installing Adonis

First thing first, we’ll need to create a new adonis app. Simply run

npm init adonis-ts-app@latest your-app-name

If you’re greeted with this prompt, choose web.

CUSTOMIZE PROJECT
❯ Select the project structure …  Press <ENTER> to select
  api   (Tailored for creating a REST API server)
▸ web   (Traditional web application with server-rendered templates)
  slim  (A smallest possible AdonisJS application)

Pick whatever project name you like. Let’s leave it as the default.

CUSTOMIZE PROJECT
❯ Select the project structure · web
❯ Enter the project name · my-app-name

You’ll be prompted to set up eslint/prettier or not. Either way is fine, but I personally prefer to set it to true (press y for true, n for false)

CUSTOMIZE PROJECT
❯ Select the project structure · web
❯ Enter the project name · my-app-name
❯ Setup eslint? (y/N) · true
❯ Setup prettier? (y/N) · true

We’ll also say true to the Webpack Encore setup.

CUSTOMIZE PROJECT
❯ Select the project structure · web
❯ Enter the project name · perpus
❯ Setup eslint? (y/N) · true
❯ Setup prettier? (y/N) · true
❯ Configure webpack encore for compiling frontend assets? (y/N) ‣ true

After that, it will set up the project and you’ll just need to cd to the project directory when it’s done. In this case, it’s my-app-name.

Installing inertia-adonisjs Adapter

Inertia only supports Laravel and Ruby on Rails officially, but there are a lot of community adapters. One of them is inertia-adonisjs which we will use in our project. Simply install it like a regular package and configure it using ace.

# install the package using npm
npm i @eidellev/inertia-adonisjs

# configure the package using ace
node ace configure @eidellev/inertia-adonisjs

It will give you several questions. Make sure you choose Svelte, you can leave the rest of them as default.

❯ Select the view you would like to use · app
❯ Would you like to install Inertia.js? (Y/n) · true
❯ Which client-side adapter would you like to set up? …  Press <ENTER> to select
  Vue 2
  Vue 3
  React
▸ Svelte

Installing Svelte

We’ll need the svelte-loader and svelte-preprocess for our app. As usual, install it using npm.

npm i svelte-loader svelte-preprocess @babel/plugin-syntax-dynamic-import

We need @babel/plugin-syntax-dynamic-import to be able to use the import syntax required for code splitting.

Configuration

Webpack configuration

We’ll add svelte-loader and svelte-preprocess to our webpack configuration. Open up webpack.config.js in your favourite editor and add these lines.

const { join } = require("path");
const Encore = require("@symfony/webpack-encore");
// add this line
const sveltePreprocess = require("svelte-preprocess");

/****the rest of the config****/

// add this block
Encore.addLoader({
  test: /\.svelte$/,
  loader: "svelte-loader",
  options: {
    emitCss: true,
    preprocess: sveltePreprocess({}),
  },
});

/****the rest of the config****/

const config = Encore.getWebpackConfig();
config.infrastructureLogging = {
  level: "warn",
};
config.stats = "errors-warnings";
// add this line
config.output.chunkFilename = "js/[name].js?id=[chunkhash]";

Inertia configuration

To use Svelte, we need to initialise our app. To do that, go to resources/js/app.js and add this snippet.

import { createInertiaApp } from "@inertiajs/inertia-svelte";
import { InertiaProgress } from "@inertiajs/progress";

InertiaProgress.init({
  showSpinner: true,
});

createInertiaApp({
  resolve: (name) => import(`./Pages/${name}.svelte`),
  setup({ el, App, props }) {
    new App({ target: el, props });
  },
});

We also added @inertiajs/progress for the loading progress because SPA app doesn’t trigger the browser’s loading progress and it can become annoying when your app navigation is slow. This is a bad UX.

Our Svelte files will live inside resources/js/Pages directory. When we want to render our app, we’ll just reference its name and it will automatically get resolved by the resolve function. For example, Foo will resolve to ./Pages/Foo.svelte.

We use import instead of require because we want to enable code splitting.

Adding assets

Don’t forget to link the javascript into our main entry point. Otherwise, there won’t be anything rendered on the page. Simply add this in resources/views/app.edge.

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <link rel="icon" type="image/png" href="/favicon.ico">
  <title>App Title</title>

  <!-- add this line -->
  <script src="{{ asset('assets/app.js') }}" defer></script>
</head>
<body>
  @inertia()
</body>
</html>

Usage

Finally, let’s see if it actually works. Create a new Svelte file in resources/js/Pages/Foo.svelte with this content.

<script>
export let text
</script>

<h1>Your Text: {text}</h1>

Open start/routes.ts and add a new route for our Svelte app.

Route.get("/foo", async ({ inertia }) => {
  return inertia.render("Foo", { text: "Hello, World!" });
});

As you can see, passing props is as simple as passing our data to the second argument. This data can come from anywhere, whether it be hardcoded, from the database, etc.

Try running the server using npm run dev or node ace serve --watch.

If we open http://localhost:3333/foo (3333 is the default port, you can change it in .env), you should see a big text saying Your Text: Hello, World! which means our app is working!

References

Of course, always refer to Adonis/Svelte/Inertia for more detailed API references, they all have extensive documentation (and a good website, too).

Here are some references I used when trying this out.

Closing Note

Inertia is an interesting library indeed. It might not be suitable for every use case but if you want to build an app where you want to use a SPA library but don’t bother with fetching the data from the API then this is perfect for you.

Hopefully this post helped you and have a nice day! :)

Comments