{"id":13417499,"url":"https://github.com/frandiox/vite-ssr","last_synced_at":"2025-05-15T07:03:05.205Z","repository":{"id":37701743,"uuid":"307743301","full_name":"frandiox/vite-ssr","owner":"frandiox","description":"Use Vite for server side rendering in Node","archived":false,"fork":false,"pushed_at":"2024-08-13T19:03:35.000Z","size":1691,"stargazers_count":839,"open_issues_count":38,"forks_count":93,"subscribers_count":15,"default_branch":"master","last_synced_at":"2025-05-12T07:16:25.896Z","etag":null,"topics":["cloudflare","ssr","vite","worker"],"latest_commit_sha":null,"homepage":"","language":"TypeScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/frandiox.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":".github/contributing.md","funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2020-10-27T15:23:36.000Z","updated_at":"2025-05-11T00:04:02.000Z","dependencies_parsed_at":"2024-08-13T22:07:48.552Z","dependency_job_id":null,"html_url":"https://github.com/frandiox/vite-ssr","commit_stats":{"total_commits":391,"total_committers":23,"mean_commits":17.0,"dds":"0.13810741687979544","last_synced_commit":"916e2fb7fd1a07a64e5f25b657f8aae0afb601e8"},"previous_names":[],"tags_count":51,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/frandiox%2Fvite-ssr","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/frandiox%2Fvite-ssr/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/frandiox%2Fvite-ssr/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/frandiox%2Fvite-ssr/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/frandiox","download_url":"https://codeload.github.com/frandiox/vite-ssr/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":253692559,"owners_count":21948321,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["cloudflare","ssr","vite","worker"],"created_at":"2024-07-30T22:00:38.486Z","updated_at":"2025-05-15T07:03:05.117Z","avatar_url":"https://github.com/frandiox.png","language":"TypeScript","funding_links":[],"categories":["SSR","Vite","TypeScript","Plugins"],"sub_categories":["Libraries","SSR"],"readme":"\u003cp align=\"center\"\u003e\n  \u003cimg width=\"180\" src=\"./logo.svg\" alt=\"Vite SSR logo\"\u003e\n\u003c/p\u003e\n\n# Vite SSR\n\nSimple yet powerful Server Side Rendering for Vite 2 in Node.js (Vue \u0026 React).\n\n- ⚡ Lightning Fast HMR (powered by Vite, even in SSR mode).\n- 💁‍♂️ Consistent DX experience abstracting most of the SSR complexity.\n- 🔍 Small library, unopinionated about your page routing and API logic.\n- 🔥 Fast and SEO friendly thanks to SSR, with SPA takeover for snappy UX.\n- 🧱 Compatible with Vite's plugin ecosystem such as file-based routing, PWA, etc.\n\nVite SSR can be deployed to any Node.js or browser-like environment, including serverless platforms like Vercel, Netlify, or even Cloudflare Workers. It can also run with more traditional servers like Express.js or Fastify.\n\n\u003e Vite SSR is unopinionated about your API logic so you must bring your own. If you want a more opiniated and fullstack setup with filesystem-based API endpoints and auto-managed edge cache, have a look at [Vitedge](https://github.com/frandiox/vitedge). It wraps Vite SSR and can be deployed to Cloudflare Workers or any Node.js environment.\n\nStart a new SSR project right away with filesystem routes, i18n, icons, markdown and more with [Vitesse (Vue)](https://github.com/frandiox/vitesse-ssr-template) or [Reactesse (React)](https://github.com/frandiox/reactesse-ssr-template). See [live demo](https://vitesse-ssr.vercel.app/).\n\n## Installation\n\nCreate a normal [Vite](https://vitejs.dev/guide/) project for Vue or React.\n\n```sh\nyarn create vite --template [vue|vue-ts|react|react-ts]\n```\n\nThen, add `vite-ssr` with your package manager (direct dependency) and your framework router.\n\n```sh\n# For Vue\nyarn add vite-ssr vue@3 vue-router@4 @vueuse/head@^0.9.0\n\n# For React\nyarn add vite-ssr react@16 react-router-dom@5\n```\n\nMake sure that `index.html` contains a root element with id `app`: `\u003cdiv id=\"app\"\u003e\u003c/div\u003e` (or change the default container id in plugin options: `options.containerId`).\n\n## Usage\n\nAdd Vite SSR plugin to your Vite config file (see [`vite.config.js`](./examples/vue/vite.config.js) for a full example).\n\n```js\n// vite.config.js\nimport vue from '@vitejs/plugin-vue'\nimport viteSSR from 'vite-ssr/plugin.js'\n// import react from '@vitejs/plugin-react'\n\nexport default {\n  plugins: [\n    viteSSR(),\n    vue(), // react()\n  ],\n}\n```\n\nThen, simply import the main Vite SSR handler in your main entry file as follows. See full examples for [Vue](./examples/vue/src/main.js) and [React](./examples/react/src/main.jsx).\n\n```js\nimport App from './App' // Vue or React main app\nimport routes from './routes'\nimport viteSSR from 'vite-ssr'\n// or from 'vite-ssr/vue' or 'vite-ssr/react', which slightly improves typings\n\nexport default viteSSR(App, { routes }, (context) =\u003e {\n  /* Vite SSR main hook for custom logic */\n  /* const { app, router, initialState, ... } = context */\n})\n```\n\nThat's right, in Vite SSR **there's only 1 single entry file** by default 🎉. It will take care of providing your code with the right environment.\n\nIf you need conditional logic that should only run in either client or server, use Vite's `import.meta.env.SSR` boolean variable and the tree-shaking will do the rest.\n\nThe third argument is Vite SSR's main hook, which runs only once at the start. It receives the SSR context and can be used to initialize the app or setup anything like state management or other plugins. See an example of [Vue + Pinia here](https://pinia.esm.dev/ssr/#state-hydration). In React, the same SSR Context is passed to the main App function/component as props.\n\n\u003cdetails\u003e\u003csummary\u003eAvailable options\u003c/summary\u003e\n\u003cp\u003e\n\nThe previous handler accepts the following options as its second argument:\n\n- `routes`: Array of routes, according to each framework's router (see [`vue-router`](https://next.router.vuejs.org/api/#routerecordraw) or [`react-router-config`](https://www.npmjs.com/package/react-router-config#route-configuration-shape)).\n- `base`: Function that returns a string with the router base. Can be useful for i18n routes or when the app is not deployed at the domain root.\n- `routerOptions`: Additional router options like scrollBehavior in [`vue-router`](https://next.router.vuejs.org/guide/advanced/scroll-behavior.html).\n- `transformState`: Modify the state to be serialized or deserialized. See [State serialization](#state-serialization) for more information.\n- `pageProps.passToPage`: Whether each route's `initialState` should be automatically passed to the page components as props.\n- `styleCollector`: Only in React. Mechanism to extract CSS in JS. See [integrations#React-CSS-inJS](#React-CSS-in-JS).\n- `debug.mount`: Pass `false` to prevent mounting the app in the client. You will need to do this manually on your own but it's useful to see differences between SSR and hydration.\n\n\u003c/p\u003e\n\u003c/details\u003e\n\n\u003cdetails\u003e\u003csummary\u003eSSR Context\u003c/summary\u003e\n\u003cp\u003e\n\nThe context passed to the main hook (and to React's root component) contains:\n\n- `initialState`: Object that can be mutated during SSR to save any data to be serialized. This same object and data can be read in the browser.\n- `url`: Initial [URL](https://developer.mozilla.org/en-US/docs/Web/API/URL).\n- `isClient`: Boolean similar to `import.meta.env.SSR`. Unlike the latter, `isClient` does not trigger tree shaking.\n- `request`: Available during SSR.\n- `redirect`: Isomorphic function to redirect to a different URL.\n- `writeResponse`: Function to add status or headers to the `response` object (only in backend).\n- `router`: Router instance in Vue, and a custom router in React to access the routes and page components.\n- `app`: App instance, only in Vue.\n- `initialRoute`: Initial Route object, only in Vue.\n\nThis context can also be accesed from any component by using `useContext` hook:\n\n```js\nimport { useContext } from 'vite-ssr'\n\n//...\nfunction() {\n  // In a component\n  const { initialState, redirect } = useContext()\n  // ...\n}\n```\n\n\u003c/p\u003e\n\u003c/details\u003e\n\n\u003cdetails\u003e\u003csummary\u003eUsing separate entry files\u003c/summary\u003e\n\u003cp\u003e\n\nEven though Vite SSR uses 1 single entry file by default, thus abstracting complexity from your app, you can still have separate entry files for client and server if you need more flexibility. This can happen when building a library on top of Vite SSR, for example.\n\nSimply provide the entry file for the client in `index.html` (as you would normally do in an SPA) and pass the entry file for the server as a CLI flag: `vite-ssr [dev|build] --ssr \u003cpath/to/entry-server\u003e`.\n\nThen, import the main SSR handlers for the entry files from `vite-ssr/vue/entry-client` and `vite-ssr/vue/entry-server` instead. Use `vite-ssr/react/*` for React.\n\n\u003c/p\u003e\n\u003c/details\u003e\n\n## SSR initial state and data fetching\n\nThe SSR initial state is the application data that is serialized as part of the server-rendered HTML for later hydration in the browser. This data is normally gathered using fetch or DB requests from your API code.\n\nVite SSR initial state consists of a plain JS object that is passed to your application and can be modified at will during SSR. This object will be serialized and later hydrated automatically in the browser, and passed to your app again so you can use it as a data source.\n\n```js\nexport default viteSSR(App, { routes }, ({ initialState }) =\u003e {\n  if (import.meta.env.SSR) {\n    // Write in server\n    initialState.myData = 'DB/API data'\n  } else {\n    // Read in browser\n    console.log(initialState.myData) // =\u003e 'DB/API data'\n  }\n\n  // Provide the initial state to your stores, components, etc. as you prefer.\n})\n```\n\nIf you prefer having a solution for data fetching out of the box, have a look at [Vitedge](https://github.com/frandiox/vitedge). Otherwise, you can implement it as follows:\n\n\u003cdetails\u003e\u003csummary\u003eInitial state in Vue\u003c/summary\u003e\n\u003cp\u003e\n\nVue has multiple ways to provide the initial state to Vite SSR:\n\n- Calling your API before entering a route (Router's `beforeEach` or `beforeEnter`) and populate `route.meta.state`. Vite SSR will get the first route's state and use it as the SSR initial state. See a full example [here](./examples/vue/src/main.js).\n\n```js\nexport default viteSSR(App, { routes }, async ({ app }) =\u003e {\n  router.beforEach(async (to, from) =\u003e {\n    if (to.meta.state) {\n      return // Already has state\n    }\n\n    const response = await fetch('my/api/data/' + to.name)\n\n    // This will modify initialState\n    to.meta.state = await response.json()\n  })\n})\n```\n\n- Calling your API directly from Vue components using [`Suspense`](https://v3.vuejs.org/guide/migration/suspense.html), and storing the result in the SSR initial state. See a full example with `Suspense` [here](./examples/vue/src/pages/Homepage.vue). If you prefer Axios, there's also an example [here](https://github.com/frandiox/vite-ssr/discussions/66).\n\n```js\nimport { useContext } from 'vite-ssr'\nimport { useRoute } from 'vue-router'\nimport { inject, ref } from 'vue'\n\n// This is a custom hook to fetch data in components\nexport async function useFetchData(endpoint) {\n  const { initialState } = useContext()\n  const { name } = useRoute() // this is just a unique key\n  const state = ref(initialState[name] || null)\n\n  if (!state.value) {\n    state.value = await (await fetch(endpoint)).json()\n\n    if (import.meta.env.SSR) {\n      initialState[name] = state.value\n    }\n  }\n\n  return state\n}\n```\n\n```js\n// Page Component with Async Setup\nexport default {\n  async setup() {\n    const state = await useFetchData('my-api-endpoint')\n    return { data }\n  },\n}\n\n// Use Suspense in your app root\n\u003ctemplate\u003e\n  \u003cRouterView v-slot=\"{ Component }\"\u003e\n    \u003cSuspense\u003e\n      \u003ccomponent :is=\"Component\" /\u003e\n    \u003c/Suspense\u003e\n  \u003c/RouterView\u003e\n\u003c/template\u003e\n```\n\n- Calling your API directly from Vue components using Vue's [`serverPrefetch`](https://ssr.vuejs.org/api/#serverprefetch), and storing the result in the SSR initial state. It's also possible to recreate [`asyncData` à la Nuxt.js](https://github.com/frandiox/vite-ssr/discussions/46#discussioncomment-988827).\n\n```js\n// Main\nexport default viteSSR(App, { routes }, ({ app, initialState }) =\u003e {\n  // You can pass it to your state management\n  // or use `useContext()` like in the Suspense example\n  const pinia = createPinia()\n\n  // Sync initialState with the store:\n  if (import.meta.env.SSR) {\n    initialState.pinia = pinia.state.value\n  } else {\n    pinia.state.value = initialState.pinia\n  }\n\n  app.use(pinia)\n})\n\n// Page Component with Server Prefetch\nexport default {\n  beforeMount() {\n    // In browser\n    this.fetchMyData()\n  },\n  async serverPrefetch() {\n    // During SSR\n    await this.fetchMyData()\n  },\n  methods: {\n    fetchMyData() {\n      const store = useStore()\n      if (!store.myData) {\n        return fetch('my/api/data').then(res =\u003e res.json()).then((myData) =\u003e {\n          store.myData = myData\n        })\n      }\n    },\n  },\n}\n```\n\n\u003c/p\u003e\n\u003c/details\u003e\n\n\u003cdetails\u003e\u003csummary\u003eInitial state in React\u003c/summary\u003e\n\u003cp\u003e\n\nThere are a few ways to provide initial state in React:\n\n- Call your API and throw a promise in order to leverage React's Suspense (in both browser and server) anywhere in your components. Vite SSR is already adding Suspense to the root so you don't need to provide it.\n\n```jsx\nfunction App({ initialState }) {\n  if (!initialState.ready) {\n    const promise = getPageProps(route).then((state) =\u003e {\n      Object.assign(initialState, state)\n      initialState.ready = true\n    })\n\n    // Throw the promise so Suspense can await it\n    throw promise\n  }\n\n  return \u003cdiv\u003e{initialState}\u003c/div\u003e\n}\n```\n\n- Calling your API before entering a route and populate `route.meta.state`. Vite SSR will get the first route's state and use it as the SSR initial state. See a full example [here](./examples/react/src/api.jsx).\n\n```jsx\nfunction App({ router }) {\n  // This router is provided by Vite SSR.\n  // Use it to render routes and save initial state.\n\n  return (\n    \u003cRoutes\u003e\n      {router.routes.map((route) =\u003e {\n        if (!route.meta.state) {\n          // Call custom API and return a promise\n          const promise = getPageProps(route).then((state) =\u003e {\n            // This is similar to modifying initialState in the previous example\n            route.meta.state = state\n          })\n\n          // Throw the promise so Suspense can await it\n          throw promise\n        }\n\n        return (\n          \u003cRoute key={route.path} path={route.path} element={\n            \u003croute.component props={...route.meta.state} /\u003e\n          } /\u003e\n        )\n      })}\n    \u003c/Routes\u003e\n  )\n}\n```\n\n\u003c/p\u003e\n\u003c/details\u003e\n\n### State serialization\n\nVite SSR simply uses `JSON.stringify` to serialize the state, escapes certain characters to prevent XSS and saves it in the DOM. This behavior can be overriden by using the `transformState` hook in case you need to support dates, regexp or function serialization:\n\n```js\nimport viteSSR from 'vite-ssr'\nimport App from './app'\nimport routes from './routes'\n\nexport default viteSSR(App, {\n  routes,\n  transformState(state, defaultTransformer) {\n    if (import.meta.env.SSR) {\n      // Serialize during SSR by using,\n      // for example, using @nuxt/devalue\n      return customSerialize(state)\n\n      // -- Or use the defaultTransformer after modifying the state:\n      // state.apolloCache = state.apolloCache.extract()\n      // return defaultTransformer(state)\n    } else {\n      // Deserialize in browser\n      return customDeserialize(state)\n    }\n  },\n})\n```\n\n## Accessing `response` and `request` objects\n\nIn development, both `response` and `request` objects are passed to the main hook during SSR:\n\n```js\nexport default viteSSR(\n  App,\n  { routes },\n  ({ initialState, request, response }) =\u003e {\n    // Access request cookies, etc.\n  }\n)\n```\n\nIn production, you control the server so you must pass these objects to the rendering function in order to have them available in the main hook:\n\n```js\nimport render from './dist/server'\n\n//...\n\nconst { html } = await render(url, {\n  manifest,\n  preload: true,\n  request,\n  response,\n  // Anything here will be available in the main hook.\n  initialState: { hello: 'world' }, // Optional prefilled state\n})\n```\n\nBeware that, in development, Vite uses plain Node.js + Connect for middleware. Therefore, the `request` and `response` objects might differ from your production environment if you use any server framework such as Fastify, Express.js or Polka. If you want to use your own server during development, check [Middleware Mode](#middleware-mode).\n\n### Editing Response and redirects\n\nIt's possible to set status and headers to the response with `writeResponse` utility. For redirects, the `redirect` utility works both in SSR (server redirect) and browser (history push):\n\n```js\nimport { useContext } from 'vite-ssr'\n\n// In a component\nfunction () {\n  const { redirect, writeResponse } = useContext()\n\n  if (/* ... */) {\n    redirect('/another-page', 302)\n  }\n\n  if (import.meta.env.SSR \u0026\u0026 /* ... */) {\n    writeResponse({\n      status: 404,\n      headers: {}\n    })\n  }\n\n  // ...\n}\n```\n\nIn the browser, this will just behave as a normal Router push.\n\n## Head tags and global attributes\n\nUse your framework's utilities to handle head tags and attributes for html and body elements.\n\n\u003cdetails\u003e\u003csummary\u003eVue Head\u003c/summary\u003e\n\u003cp\u003e\n\nInstall [`@vueuse/head`](https://github.com/vueuse/head) as follows:\n\n```js\nimport { createHead } from '@vueuse/head'\n\nexport default viteSSR(App, { routes }, ({ app }) =\u003e {\n  const head = createHead()\n  app.use(head)\n\n  return { head }\n})\n\n// In your components:\n// import { useHead } from '@vueuse/head'\n// ... useHead({ ... })\n```\n\n\u003c/p\u003e\n\u003c/details\u003e\n\n\u003cdetails\u003e\u003csummary\u003eReact Helmet\u003c/summary\u003e\n\u003cp\u003e\n\nUse [`react-helmet-async`](https://github.com/staylor/react-helmet-async) from your components (similar usage to `react-helmet`). The provider is already added by Vite SSR.\n\n```jsx\nimport { Helmet } from 'react-helmet-async'\n\n// ...\n;\u003c\u003e\n  \u003cHelmet\u003e\n    \u003chtml lang=\"en\" /\u003e\n    \u003cmeta charSet=\"utf-8\" /\u003e\n    \u003ctitle\u003eHome\u003c/title\u003e\n    \u003clink rel=\"canonical\" href=\"http://mysite.com/example\" /\u003e\n  \u003c/Helmet\u003e\n\u003c/\u003e\n```\n\n\u003c/p\u003e\n\u003c/details\u003e\n\n## Rendering only in client/browser\n\nVite SSR exports `ClientOnly` component that renders its children only in the browser:\n\n```jsx\nimport { ClientOnly } from 'vite-ssr'\n\n//...\n;\u003cdiv\u003e\n  \u003cClientOnly\u003e\n    \u003cdiv\u003e...\u003c/div\u003e\n  \u003c/ClientOnly\u003e\n\u003c/div\u003e\n```\n\n## Development\n\nThere are two ways to run the app locally for development:\n\n- SPA mode: `vite dev` command runs Vite directly without any SSR.\n- SSR mode: `vite-ssr dev` command spins up a local SSR server. It supports similar attributes to Vite CLI, e.g. `vite-ssr --port 1337 --open`.\n\nSPA mode will be slightly faster but the SSR one will have closer behavior to a production environment.\n\n### Middleware Mode\n\nIf you want to run your own dev server (e.g. Express.js) instead of Vite's default Node + Connect, you can use Vite SSR in middleware mode:\n\n```js\nconst express = require('express')\nconst { createSsrServer } = require('vite-ssr/dev')\n\nasync function createServer() {\n  const app = express()\n\n  // Create vite-ssr server in middleware mode.\n  const viteServer = await createSsrServer({\n    server: { middlewareMode: 'ssr' },\n  })\n\n  // Use vite's connect instance as middleware\n  app.use(viteServer.middlewares)\n\n  app.listen(3000)\n}\n\ncreateServer()\n```\n\n## Production\n\nRun `vite-ssr build` for buildling your app. This will create 2 builds (client and server) that you can import and use from your Node backend. See an Express.js example server [here](./examples/node-server/index.js), or a serverless function deployed to Vercel [here](https://github.com/frandiox/vitesse-ssr-template/blob/master/serverless/api/index.js).\n\n\u003cdetails\u003e\u003csummary\u003eKeeping index.html in the client build\u003c/summary\u003e\n\u003cp\u003e\n\nIn an SSR app, `index.html` is already embedded in the server build, and is thus removed from the client build in order to prevent serving it by mistake. However, if you would like to keep `index.html` in the client build (e.g. when using server side routing to selectively use SSR for a subset of routes), you can set `build.keepIndexHtml` to `true` in the plugin options:\n\n```js\n// vite.config.js\n\nexport default {\n  plugins: [\n    viteSSR({\n      build: {\n        keepIndexHtml: true,\n      },\n    }),\n    [...]\n  ],\n}\n```\n\n\u003c/p\u003e\n\u003c/details\u003e\n\n## Integrations\n\nCommon integrations will be added here:\n\n### React CSS in JS\n\nUse the `styleCollector` option to specify an SSR style collector. `vite-ssr` exports 3 common CSS-in-JS integrations: `styled-components`, `material-ui-core-v4` and `emotion`:\n\n```js\nimport viteSSR from 'vite-ssr/react'\nimport styleCollector from 'vite-ssr/react/style-collectors/emotion'\n\nexport default viteSSR(App, { routes, styleCollector })\n```\n\nYou can provide your own by looking at the [implementation](./src/react/style-collectors/) of any of the existing collectors.\n\nNote that you still need to install all the required dependencies from these packages (e.g. `@emotion/server`, `@emotion/react` and `@emotion/cache` when using Emotion).\n\n## Custom Typings\n\nYou can define your own typings with `vite-ssr`. To declare custom types, the file mostly needs to `import` or `export` something not to break other types.\nExample transforming `request` and `response` to types of `express`:\n\n```ts\nimport { Request, Response } from 'express'\n\ndeclare module 'vite-ssr/vue' {\n  export interface Context {\n    request: Request\n    response: Response\n  }\n}\n```\n\n## Community contributions\n\nFeel free to submit your projects:\n\n### Templates\n\n- Vue 3, Vercel, Axios. [Link](https://github.com/kadiryazici/vite-ssr-vue3-example/).\n\n### Addons\n\n- `vite-ssr-middleware`: Add route middlewares for `vite-ssr` and Vue, similar to Nuxt. [Link](https://github.com/kadiryazici/vite-ssr-middleware).\n\n### Examples\n\n- Imitating Nuxt's `asyncData` in Vue options API. [Link](https://github.com/frandiox/vite-ssr/discussions/46#discussioncomment-988827).\n- Fetch data from Vue components with composition API hook and Axios. [Link](https://github.com/frandiox/vite-ssr/discussions/66#discussion-3467130).\n- Vue + TypeScript with API calls. [Link](https://github.com/thruthesky/vite-ssr/tree/vue-ts/examples/vue-ts).\n- Vue + TypeScript using `serverPrefetch`. [Link](https://github.com/thruthesky/vite-ssr/tree/vue-ts/examples/vue-ts-server-prefetch).\n\n## References\n\nThe following projects served as learning material to develop this tool:\n\n- [@tbgse](https://github.com/tbgse)'s [vue3-vite-ssr-example](https://github.com/tbgse/vue3-vite-ssr-example/)\n\n## Todos\n\n- [x] TypeScript\n- [x] Make `src/main.js` file name configurable\n- [x] Support build options as CLI flags (`--ssr entry-file` supported)\n- [x] Support React\n- [x] SSR dev-server\n- [x] Make SSR dev-server similar to Vite's dev-server (options, terminal output)\n- [x] Research if `vite-ssr` CLI logic can be moved to the plugin in Vite 2 to use `vite` command instead.\n- [x] Docs\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ffrandiox%2Fvite-ssr","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Ffrandiox%2Fvite-ssr","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ffrandiox%2Fvite-ssr/lists"}