Skip to content

matrei/grails-inertia-plugin

Folders and files

NameName
Last commit message
Last commit date
Aug 8, 2024
Feb 7, 2024
Nov 29, 2024
Feb 27, 2024
Feb 12, 2024
Oct 14, 2022
Oct 14, 2022
Jan 14, 2024
Dec 21, 2023
Apr 13, 2024
Feb 27, 2024
Nov 26, 2023
Nov 26, 2023
Nov 25, 2023
Nov 27, 2023
Aug 6, 2024

Repository files navigation

Grails Adapter for Inertia.js

Maven Central Java CI

Grails plugin for using Inertia.js to build single-page apps without building an API.

What is Inertia.js?

Inertia.js lets you, in its own words, “quickly build modern single-page React, Vue and Svelte apps using classic server-side routing and controllers”.

Using Inertia.js allows using your favorite MVC server-side framework (Grails obviously) with your favorite client-side SPA framework - no need to build a separate API.

Demo application

Ping CRM is an application using this plugin
Source | Live Demo

Note

This is a port to Grails/Groovy of the original Ping CRM demo written in Laravel/PHP.

Screenshot of the Ping CRM application

Plugin Installation

If you don't have an application already:

grails create-app myapp
cd myapp


Add the plugin dependency to the project:

// myapp/build.gradle
dependencies {
    //...
    // Replace $inertiaPluginVersion with a suitable release version for your project, or define it in ~/myapp/gradle.properties
    implementation "io.github.matrei:grails-inertia-plugin:$inertiaPluginVersion"
    //...
}

Note

For a Grails 5/Java 8 - use the latest version of the plugin with major version 1.
For Grails 6/Java 11 - use the latest version of the plugin.


To add the client dependencies and workflow to a Grails project, create the following files: (Vue 3 example)

// myapp/package.json (versions @ 2023-11-25) 
{
  "name": "myapp",
  "version": "0.1.0",
  "private": true,
  "scripts": {
    "serve": "vite --port 3000",
    "build": "vite build && vite build --outDir src/main/resources/ssr --ssr src/main/javascript/ssr.js"
  },
  "dependencies": {
    "vue": "^3.3.9",
    "@inertiajs/vue3": "^1.0.14"
  },
  "devDependencies": {
    "@vitejs/plugin-vue": "^4.5.0",
    "vite": "^5.0.2"
  }
}
// myapp/vite.config.js
import { fileURLToPath, URL } from 'node:url'
import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'

export default defineConfig(({ command }) => ({
  base: command === 'serve' ? '' : '/static/dist/',
  publicDir: false,
  build: {
    manifest: true,
    outDir: 'src/main/resources/public/dist',
    assetsDir: 'js',
    rollupOptions: {
      input: 'src/main/javascript/main.js'
    }
  },
  plugins: [vue()],
  resolve: {
    alias: {
      '@': fileURLToPath(new URL('./src/main/javascript', import.meta.url))
    }
  },
  server: {
    // Needed for changes to picked up when running in WSL on Windows
    watch: {
      usePolling: true
    }
  }
}))
// myapp/src/main/javascript/main.js
import { createApp, h } from 'vue'
import { createInertiaApp } from '@inertiajs/vue3'

createInertiaApp({
  resolve: async (name) => {
    const pages = import.meta.glob('./Pages/**/*.vue')
    return (await pages[`./Pages/${name}.vue`]()).default
  },
  setup ({el, App, props, plugin}) {
    createApp({ render: () => h(App, props) })
      .use(plugin)
      .mount(el)
  }
})
// myapp/src/main/javascript/ssr.js (Optional, for Server Side Rendering)
import { createSSRApp, h } from 'vue'
import { createInertiaApp } from '@inertiajs/vue3'
import createServer from '@inertiajs/vue3/server'
import { renderToString } from '@vue/server-renderer'

createServer(page =>
    createInertiaApp({
        page,
        render: renderToString,
        resolve: async (name) => {
            const pages = import.meta.glob('./Pages/**/*.vue')
            return (await pages[`./Pages/${name}.vue`]()).default
        },
        setup({ App, props, plugin }) {
            return createSSRApp({
                render: () => h(App, props)
            })
            .use(plugin)
        }
    })
)


It can be a good idea to add the following entries to your .gitignore

# myapp/.gitignore
# ...
node_modules
src/main/resources/public/dist


And run the following command to install the client dependencies:

npm install

Usage

In your Grails controllers, you can now select which JavaScript Page Component to render and pass the values of the props to it.

// myapp/grails-app/controllers/myapp/BookController.groovy
package myapp

class BookController {
    
    def index() {
        def books = ['Grails in Action', 'Programming Grails', 'The Definitive Guide to Grails 2']
        renderInertia 'Books/Index', [books: books]
    }
}

Here is an example Vue 3 Single File Component to that will render the books as a list.

<!-- myapp/src/main/javascript/Pages/Books/Index.vue -->
<script setup>
defineProps({ books: Array })
</script>
<template>
  <ul>
    <li v-for="book in books">{{ book }}</li>
  </ul>
</template>


For development with Hot Module Replacement of the application run: (in separate terminals)

npm run serve
./gradlew bootRun


For production or test, first build the production version of your JavaScript app...

npm run build


...and then run whatever you want to do:

./gradlew integrationTest
./gradlew bootJar

SSR

To enable server-side rendering, make sure a Node.js version compatible with your client-side app is installed and added to the PATH on your system and add the following to your application.yml:

inertia:
  ssr:
    enabled: true
    url: 'http://localhost:13714/render'
    bundle: 'src/main/resources/ssr/ssr.mjs'