DEV Community

Cover image for Building a Server-Side Rendering (SSR) Plugin for Vite
EZ
EZ

Posted on

Building a Server-Side Rendering (SSR) Plugin for Vite

Server-Side Rendering (SSR) is a powerful technique for improving the performance and SEO of modern web applications. Vite provides excellent support for SSR out of the box, but you can extend its functionality by creating custom plugins tailored to your project's needs.

In this post, we’ll walk through creating a custom Vite plugin to enhance SSR support. This plugin will help you streamline SSR-specific logic, such as handling server-side entry points and optimizing SSR builds.


Why Build an SSR Plugin for Vite?

While Vite supports SSR natively, you might want to:

  • Automate the creation of SSR entry points.
  • Optimize SSR builds for performance.
  • Inject SSR-specific environment variables.
  • Add custom middleware or server logic.

By building a custom plugin, you can encapsulate these tasks and make your SSR setup more maintainable.


Example: Creating an SSR Plugin for Vite

Let’s create a custom Vite plugin that simplifies SSR setup. This plugin will:

  1. Automatically generate an SSR entry point if it doesn’t exist.
  2. Inject SSR-specific environment variables.
  3. Optimize the SSR build process.

Step 1: Set Up a Vite Project

If you don’t already have a Vite project, create one:

npm create vite@latest my-vite-ssr-app
cd my-vite-ssr-app
npm install
Enter fullscreen mode Exit fullscreen mode

Step 2: Install SSR Dependencies

Install the necessary dependencies for SSR:

npm install express --save
Enter fullscreen mode Exit fullscreen mode

Step 3: Create the SSR Plugin

Create a new file for your plugin, e.g., vite-plugin-ssr.js:

import fs from 'fs';
import path from 'path';

export default function vitePluginSsr(options = {}) {
  const { entry = 'src/entry-server.js', outDir = 'dist/server' } = options;

  return {
    name: 'vite-plugin-ssr',

    // Generate SSR entry point if it doesn't exist
    configResolved(config) {
      const entryPath = path.resolve(config.root, entry);
      if (!fs.existsSync(entryPath)) {
        fs.writeFileSync(
          entryPath,
          `export default function render() {
  return { html: '<div>Hello from SSR!</div>' };
}`
        );
        console.log(`Generated SSR entry point at ${entryPath}`);
      }
    },

    // Inject SSR-specific environment variables
    config(config, env) {
      return {
        define: {
          'process.env.SSR': JSON.stringify(env.mode === 'production'),
        },
      };
    },

    // Optimize SSR build
    buildStart() {
      console.log('Building for SSR...');
    },

    // Modify the output directory for SSR builds
    generateBundle(options, bundle) {
      if (options.format === 'cjs') {
        for (const file in bundle) {
          const chunk = bundle[file];
          if (chunk.type === 'chunk') {
            chunk.fileName = path.join(outDir, chunk.fileName);
          }
        }
      }
    },
  };
}
Enter fullscreen mode Exit fullscreen mode

Step 4: Integrate the Plugin into Your Vite Config

Open your vite.config.js file and import the plugin:

import { defineConfig } from 'vite';
import vitePluginSsr from './vite-plugin-ssr';

export default defineConfig({
  plugins: [
    vitePluginSsr({
      entry: 'src/entry-server.js', // Customize the SSR entry point
      outDir: 'dist/server', // Customize the SSR output directory
    }),
  ],
  build: {
    ssr: true, // Enable SSR build
  },
});
Enter fullscreen mode Exit fullscreen mode

Step 5: Create an SSR Server

Create a simple Express server to render your app:

// server.js
import express from 'express';
import { createServer } from 'vite';
import path from 'path';

async function createSsrServer() {
  const app = express();
  const vite = await createServer({
    server: { middlewareMode: true },
    appType: 'custom',
  });

  app.use(vite.middlewares);

  app.use('*', async (req, res) => {
    const { render } = await vite.ssrLoadModule('/src/entry-server.js');
    const { html } = render();
    res.send(`
      <!DOCTYPE html>
      <html>
        <head>
          <title>SSR with Vite</title>
        </head>
        <body>
          <div id="app">${html}</div>
        </body>
      </html>
    `);
  });

  app.listen(3000, () => {
    console.log('Server running at http://localhost:3000');
  });
}

createSsrServer();
Enter fullscreen mode Exit fullscreen mode

Step 6: Test the Plugin

Build your SSR app and start the server:

npm run build
node server.js
Enter fullscreen mode Exit fullscreen mode

Visit http://localhost:3000 to see your SSR app in action!


How the Plugin Works

  1. Entry Point Generation: The plugin checks if an SSR entry point exists and creates one if it doesn’t.
  2. Environment Variables: It injects SSR-specific environment variables, such as process.env.SSR.
  3. Build Optimization: The plugin modifies the output directory for SSR builds and logs build progress.
  4. Custom Logic: You can extend the plugin to add more SSR-specific features, such as middleware or server-side logic.

Customizing the Plugin

You can extend this plugin to suit your needs. For example:

  • Add support for custom SSR frameworks (e.g., React, Vue, Svelte).
  • Optimize SSR builds for performance (e.g., code splitting, tree-shaking).
  • Add middleware for handling SSR-specific routes or APIs.

Top comments (0)