Serlina: A progressive React serverside-rendering framework

Randy
Randy
Aug 7, 2018 · 2 min read

Since Next.js is a lightweight and well-experience serverside-rendering framework, I love using it on my side projects. It saves my time in dealing with the boilerplate code. I write React code, and it just works.

But when I want to use it on a project that runs its own server, something happened. Next.js takes the full control of the http context when app.render is called, while our server has some effects on the context.

I read the codes of Next.js and try to find something like nextjs/core but there is not.

So I decided to make a serverside-rendering framework that without running a server. It only does the compiling, rendering your pages to string. And let your own server to render the string to the client. And keep the well-experience just like Next.js.

This is what Serlina is. Let’s see how the most simple Serlina application is like:

npm i serlina react react-dom --save

Create a folder structure like:

├── index.js # Your server
├── page # Your React pages
│ └── page1.js

Your server:

// index.js

const { Serlina } = require('serlina')
const path = require('path')

const http = require('http')

const serlina = new Serlina({
baseDir: path.resolve(__dirname, './')
})

serlina.prepare()
.then(() => {
http.createServer(async (req, res) => {
res.writeHead(200, { 'Content-Type': 'text/html' })
if (req.url === '/page1') {
const rendered = await serlina.render('page1')
res.write(rendered.string)
} else {
res.write('works!')
}
res.end()
}).listen(8090)
})
.catch(console.error)

Your page:

// page/page1.js

export default () => {
return <div>Hello Serlina!</div>
}

Finally, run node index.js and visit http://localhost:8090/page1. You will see the React page.

As you can see, there are two main methods:

  • prepare() Doing the compiling works.
  • render() Render a page and get the rendered string that will be sent to the client.

Unlike Next.js, Serlina needs manually injecting the payload that you use in getInitialProps:

// index.js

const { Serlina } = require('serlina')
const path = require('path')

const http = require('http')

const serlina = new Serlina({
baseDir: path.resolve(__dirname, './')
})

serlina.prepare()
.then(() => {
http.createServer(async (req, res) => {
+ serlina.injectPayload({ req }) // manually inject. globally
res.writeHead(200, { 'Content-Type': 'text/html' })
if (req.url === '/page1') {
+ const rendered = await serlina.render('page1', { foo: 'bar' }) // manually inject. specifically.
res.write(rendered.string)
} else {
res.write('works!')
}
res.end()
}).listen(8090)
})
.catch(console.error)

Then you can use the payload:

// page/page1.js
import * as React from 'react'

export default class Page1 extends React.Component {

static async getInitialProps ({ req }) {
return {
url: req.url
}
}

render () {
return <div>You are now at {this.props.url}</div>
}
}

You can take a deep dive into Serlina through http://serlina.js.org or https://github.com/djyde/serlina

You could use it in your existing codebase easily. Just write a new router, prepare() before starting your server, render() a page and return the rendered string to the client. That is. This is why I call it progressive.

Another example is egg-serlina, which is the main reason I create Serlina. It brings the best serverside-rendering solution to Egg.js.

HackerNoon.com

how hackers start their afternoons.

Randy

Written by

Randy

Coding since my 13. Writing JavaScript. Now at Alibaba Inc.

HackerNoon.com

how hackers start their afternoons.

Welcome to a place where words matter. On Medium, smart voices and original ideas take center stage - with no ads in sight. Watch
Follow all the topics you care about, and we’ll deliver the best stories for you to your homepage and inbox. Explore
Get unlimited access to the best stories on Medium — and support writers while you’re at it. Just $5/month. Upgrade