https://github.com/coxmi/ssr-tools
A collection of tools to use in SSR rendering
https://github.com/coxmi/ssr-tools
Last synced: 6 months ago
JSON representation
A collection of tools to use in SSR rendering
- Host: GitHub
- URL: https://github.com/coxmi/ssr-tools
- Owner: coxmi
- License: other
- Created: 2023-11-22T16:00:39.000Z (over 2 years ago)
- Default Branch: main
- Last Pushed: 2025-10-20T15:50:22.000Z (9 months ago)
- Last Synced: 2025-10-22T13:24:57.167Z (9 months ago)
- Language: TypeScript
- Size: 683 KB
- Stars: 1
- Watchers: 1
- Forks: 0
- Open Issues: 0
-
Metadata Files:
- Readme: README.md
Awesome Lists containing this project
README
Note: This library is a work in progress
# ssr-tools
Some tools to use in SSR-rendered apps, designed primarily around islands, vite, and a next-style file router.
### Using vite:
```js
import preact from '@preact/preset-vite'
import { islands, fileRouter, client } from 'ssr-tools'
defineConfig({
plugins: [
preact(),
islands(),
fileRouter(),
client(),
],
build: {
ssr: true
}
})
```
## Plugins
### `islands()`
Import components as islands with a simple import:
```ts
import { ComplexComponent } from './button.ts?island'
// basic props are automatically serialised and a client-side bundle is
// added to the manifest, with only the used island components
```
By default this works with Preact only, but the provider interface is simple enough that you can build one yourself for other frameworks ([see Preact example](https://github.com/coxmi/ssr-tools/tree/main/src/islands/providers/preact)). Just pass in your provider in `vite.config.ts`:
```ts
islands({
provider: {
ssr: {
name: 'name-of-export',
importFrom: 'path/that/resolves/to/ssr/wrapper',
importNamed: true || false
},
bundle: ({ imports, variables, code }) => `
// client bundle content goes here
`
}
})
```
### `client()`
Import anything directly into the client bundle:
```ts
import './client.ts?client'
```
### `fileRouter()`
A Next-style file router. To start, add the plugin and define a route:
`vite.config.ts`
```ts
defineConfig({
plugins: [
fileRouter({
// default options
dir: 'src/pages',
glob: '**/*.{ts,tsx,js,jsx}',
removeTrailingSlash: true
}),
],
build: {
ssr: true
}
})
```
`src/pages/[slug].tsx`
```ts
export default function page(ctx) {
return `
${ctx.params.slug}
`
}
```
Then add the file router middleware to your server:
```ts
import http from 'node:http'
import { fileRouterMiddleware } from 'ssr-tools'
const fileRouter = await fileRouterMiddleware()
const app = http.createServer((req, res) => {
fileRouter(req, res, () => res.end())
})
app.listen(port)
```
#### Supported filename patterns:
| File name | Route pattern | Matching paths |
| :-- | :-- | :-- |
| `/index.ts` | `/`| `/` |
| `/about.ts` | `/about`| `/about` |
| `/books/[slug].ts` | `/books/:slug`| `/books/foo`
`/books/bar` |
| `/books/[slug]/reviews` | `/blog/:slug/reviews`| `/blog/foo/reviews`|
| `/api/[...all].ts` | `/api/*all`| `/api/search`
`/api/docs/foo`
`/api/docs/bar`|
#### `ctx`
Context is an object passed to each route handler, with the following properties:
| Property | Description | Example/usage |
| :-- | :-- | :-- |
| `params` | Any route params from request | `{ slug: 'hello-world' }` |
| `path` | The relative path to the requested page | `/blog/hello-world` |
| `query` | [`URLSearchParams`](https://developer.mozilla.org/en-US/docs/Web/API/URLSearchParams) object | `query.get('category')` |
#### Static rendering
To render static pages, Export a `build` object to your route:
`src/pages/[slug].tsx`
```ts
export default build = {
// return an iterable, and a page will be generated for each entry
from: () => await getPages()
// specify your url params
// (`props` is each entry from the `from` function)
url: props => ({
slug: slugify(props.title)
})
}
// render the content from `ctx.props`
export default function page(ctx) {
return `
${ctx.props.title}
`
}
```
### Still to complete
- Custom handlers for `GET`/`HEAD`/`POST`/`PUT`/`DELETE`/`OPTIONS`/`PATCH`
- Web-standard `Request`/`Response` arguments in all middleware and route handlers
- `FormData` handling
- Set props for single pages in `build.props`
- Route path override for custom routes
- Render statically-generated pages from middleware
- Ability to render routes outside of Vite
- Multi-platform support
- And more…
# Contributing
Contributions welcome!
# Acknowledgements
islands plugin adapted from [vite-plugin-voie](https://github.com/brattonross/vite-plugin-voie), and [barelyhuman](https://github.com/barelyhuman)'s [preact-island-plugins](https://github.com/barelyhuman/preact-island-plugins).