# Vite plugin ssr

Like Next.js/Nuxt but as do-one-thing-do-it-well Vite plugin.

Use any UI framework (React, Vue, Svelte, Solid, ...) and any tool you want (any frontend library, web technology, deploy environment, Vite plugin, ...).

With vite-plugin-ssr, you integrate tools manually and keep architectural control.

Note

Vike replace the vite-plugin-ssr package. Ts.ED provides @tsed/vite-ssr-plugin and @tsed/vike packages. All new features will only embed to @tsed/vike package.

To migrate @tsed/vite-ssr-plugin, just replace @tsed/vite-ssr-plugin by @tsed/vike in your code and install vike dependency instead of vite-plugin-ssr.

TIP

You can read also this article over Ts.ED + Vite-plugin-ssr on Medium: https://romainlenzotti.medium.com/use-vite-and-ts-ed-to-build-your-website-84fb4c0d8079

# Features

  • Use @Vite decorator to generate a page using vite-plugin-ssr
  • Render a page using any UI framework (React, Vue, Svelte, Solid, ...)

# Quick start

# Installation

npm install vite-plugin-ssr @tsed/vite-ssr-plugin vite@4 --save
1

Then edit your Server.ts:

import {join} from "path";
import {Configuration, Inject} from "@tsed/di";
import {PlatformApplication} from "@tsed/common";
import "@tsed/platform-express"; // /!\ keep this import
import "@tsed/vite-ssr-plugin"; // add this
import "@tsed/ajv";
import "@tsed/swagger";
import {config} from "./config";
import * as rest from "./controllers/rest";
import * as pages from "./controllers/pages";

@Configuration({
  vite: {
    root: "../path/to/app/project"
  }
})
export class Server {}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17

We recommend you to start your project with the starter project (opens new window).

The start project is a monorepo (Nx) with 2 projects:

  • packages/server: the backend project
    • packages/server/controllers/pages: the controllers pages directory
    • packages/server/controllers/rest: the controllers Rest directory
  • packages/app: the frontend project
    • packages/app/pages: the pages directory
    • packages/app/renderer: the app shell directory (header, footer, layout, etc...)

# Usage

import {Constant, Controller} from "@tsed/di";
import {HeaderParams} from "@tsed/platform-params";
import {Vite} from "@tsed/vite-ssr-plugin";
import {SwaggerSettings} from "@tsed/swagger";
import {Get, Hidden, Returns} from "@tsed/schema";

@Hidden()
@Controller("/")
export class IndexController {
  @Constant("swagger")
  private swagger: SwaggerSettings[];

  @Get("/")
  @Vite()
  @Returns(200, String).ContentType("text/html")
  get(@HeaderParams("x-forwarded-proto") protocol: string, @HeaderParams("host") host: string) {
    const hostUrl = `${protocol || "http"}://${host}`;

    return {
      docs: this.swagger.map((conf) => {
        return {
          url: hostUrl + conf.path,
          ...conf
        };
      })
    };
  }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28

And his React component:

import React from "react";
import {PageContext} from "../../renderer/types";
import type {SwaggerSettings} from "@tsed/swagger"; // ! keep type import

export interface HomePageProps {
  docs: ({url: string} & SwaggerSettings)[];
}

export function Page({docs}: PageContext & HomePageProps) {
  return (
    <>
      <h1>Welcome,</h1>

      <p>This page is built with Ts.ED and vite-plugin-ssr.</p>

      <br />
      <br />

      <ul>
        {docs.map((doc) => {
          return (
            <li>
              <a href={doc.path}>
                <span>OpenSpec {doc.specVersion}</span>
              </a>
            </li>
          );
        })}
      </ul>
    </>
  );
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32

Result:

# Add a controlled page

By default, vite-plugin-ssr does Filesystem Routing:

FILESYSTEM                        URL
pages/index.page.js               /
pages/about.page.js               /about
pages/faq/index.page.js           /faq
pages/movies/@id/index.page.js    /movies/1, /movies/2, /movies/3, ...
1
2
3
4
5

So if you want to expose a movies page with the following url /movies url, create a new file packages/app/pages/movies/index.page.tsx:

import React from "react";
import {PageContext} from "../../renderer/types";

interface Movie {
  id: string;
  title: string;
}

export interface MoviesPageProps {
  movies: Movie[];
}

export function Page({movies}: PageContext & MoviesPageProps) {
  return (
    <>
      <h1>Movies,</h1>

      <ul>
        {movies.map((doc) => {
          return (
            <li>
              <a href={"/movies/" + movies.id}>
                <span>{movies.title}</span>
              </a>
            </li>
          );
        })}
      </ul>
    </>
  );
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31

TIP

You can also define so-called "Route Strings" and "Route Functions".

// /pages/movies.page.route.js

// This file defines the route of `/pages/movies.page.js`

// Route String
export default "/movies/@movieId";
1
2
3
4
5
6

Then create a new Ts.ED controller MoviesController under packages/server/src/controllers/pages to handle all request that match the "/movies" route:

import {Controller, Get} from "@tsed/common";

class Movie {
  @Property()
  id: string;

  @Property()
  title: string;
}

@Controller("/movies")
export class MoviesController {
  @Get("/")
  @Returns(200, Array).Of(Movie)
  @Vite()
  get() {
    return [
      {id: "1", title: "Movie 1"},
      {id: "2", title: "Movie 2"},
      {id: "3", title: "Movie 3"}
    ];
  }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23

# Author

    # Maintainers Help wanted

      Last Updated: 6/13/2024, 12:50:58 PM

      Other topics