JSPM

  • Created
  • Published
  • Downloads 34
  • Score
    100M100P100Q80885F
  • License MIT

A collection of tools to use in SSR rendering

Package Exports

  • ssr-tools
  • ssr-tools/file-router
  • ssr-tools/islands
  • ssr-tools/islands/preact
  • ssr-tools/islands/preact/client
  • ssr-tools/islands/preact/ssr

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:

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:

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
<ComplexComponent name={'my-component'} />

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). Just pass in your provider in vite.config.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:

import './client.ts?client'

fileRouter()

A Next-style file router. To start, add the plugin and define a route:

vite.config.ts

defineConfig({
   plugins: [
      fileRouter({
            // default options
         dir: 'src/pages',
         glob: '**/*.{ts,tsx,js,jsx}',
         removeTrailingSlash: true
      }),
   ],
   build: {
      ssr: true 
   }
})

src/pages/[slug].tsx

export default function page(ctx) {
   return `<html>
      <body>
         <h1>${ctx.params.slug}</h1>
      </body>
   </html>`
}

Then add the file router middleware to your server:

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 object query.get('category')

Static rendering

To render static pages, Export a build object to your route:

src/pages/[slug].tsx

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 `<html>
      <body>
         <h1>${ctx.props.title}</h1>
      </body>
   </html>`
}

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, and barelyhuman's preact-island-plugins.