inertiajs / inertia-rails

The Rails adapter for Inertia.js.
https://inertiajs.com
MIT License
529 stars 43 forks source link

WIP: SSR integration #73

Closed zealot128 closed 2 years ago

zealot128 commented 2 years ago

Just want to let you know, I am working on a SSR integration.

How it works:
It is based on the SSR guide recently released to Inertia backer's and Vite's SSR guide amalgamated into this prototype/ProofOfConcept. When rendering a template for the first time, it will check, if a SSR server is running and try to post /render to it with the page as a json payload. The SSR server is expected to respond with a JSON object of { head: [], body: "" }. The plugin then puts the body as the complete template, and supplies the head as instance variables for the application layout. If any error occurs alongside, the plugin will fallback to the default handling of rendering inertia template with div#app.

Configuration:

SSR is opt-in:

# config/initializers/inertia.rb
InertiaRails.ssr_enabled = true
InertiaRails.ssr_host = 'localhost'
InertiaRails.ssr_port = 23526

To add headers from rendered files (supplied by inertia-head), also add them to your application layout:

    <% if @ssr_headers %>
      <!-- SSR-headers -->
      <% @ssr_headers.each do |head| %>
        <%= raw head %>
      <% end %>
    <% end %>
 </head>

Development:

Missing:

(How to make a SSR also depends a lot on your bundler, Webpack, Vite etc., we are using Vite at the moment)

Our Vite + Inertia + Vue SSR server: ```js // server.js const fs = require("fs") const path = require("path") const express = require("express") const { createServer: createViteServer } = require("vite") async function createServer() { const app = express() app.use(express.json()) const vite = await createViteServer({ server: { middlewareMode: "ssr", port: 23526, host: '0.0.0.0', hmr: false, // hmr: { port: 23526, protocol: "wss", host: "jobsdaheim.swi.pludoni.com" }, https: false, }, }) app.use(vite.middlewares) app.post("/render", async (request, response, next) => { try { const render = await vite.ssrLoadModule("~/ssr") const out = await render.default(request.body) response.json(out) } catch (error) { // vite.ssrFixStacktrace(error) next(error) } }) app.listen(23526) } createServer() ``` ```typescript # app/javascript/ssr.ts import { renderToString } from "@vue/server-renderer" import { createInertiaApp } from "@inertiajs/inertia-vue3" import { createSSRApp, h } from "vue" export default (jsonPage: any) => { return createInertiaApp({ page: jsonPage, render: renderToString, resolve: (name) => import(`/pages/${name}.vue`), setup({ app, props, plugin }) { return createSSRApp({ render: () => h(app, props), }).use(plugin) }, }) } ```
bknoles commented 2 years ago

Awesome!! i think @BrandonShar has hacked around on this a bit too. This would be a really great addition... I've found pre-rendering for SEO purposes to be one of the biggest cons of the rich JS client / SPA approach.

hakimmazouz commented 2 years ago

Hey all! Extremely excited to see this as a fan of rails and frontend. Been dreaming of this for a while. Is there an update on this PR since last?

BrandonShar commented 2 years ago

Closing per #73, thanks so much for getting the ball rolling again here @zealot128 !

zealot128 commented 2 years ago

Closing per #73, thanks so much for getting the ball rolling again here @zealot128 !

Great to here it was of any help and the Rails integration can be equivalent to Laravel! I hadn't deployed our branch yet, because we had some problems when compiling the SSR bundle (All the code/libraries must not use the document/window/history etc.), but I am very interested in trying it out at a later point again.