# Vike

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 vike, 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 (opens new window)

# Features

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

# Quick start

# Installation

npm install vike @tsed/vike 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/vike"; // 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/vike";
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 vike.</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, vike 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: 4/26/2024, 5:13:34 PM

      Other topics