Riot module to render riot components on the server
npm i -S riot @riotjs/compiler @riotjs/ssr
You can simply render your components' markup as it follows:
import MyComponent from './my-component.js'
import render from '@riotjs/ssr'
const html = render('my-component', MyComponent, { some: 'initial props' })
Important If you want to import raw .riot
components in your application you might want to use @riotjs/register
Note that components rendered on the server will always automatically receive the isServer=true
property.
Components that can not be rendered synchronously must expose the onAsyncRendering
method to the renderAsync
function. For example:
<async-component>
<p>{ state.username }<p>
<script>
export default {
onBeforeMount({ isServer }) {
// if it's not SSR we load the user data right the way
if (!isServer) {
this.loadUser()
}
},
loadUser() {
return fetch('/user/name').then(({name}) => {
this.update({ name })
})
},
// this function will be automatically called only
// if the component is rendered via `renderAsync`
onAsyncRendering() {
return this.loadUser()
}
}
</script>
</async-component>
The above component can be rendered on the server as it follows:
import MyComponent from './async-component.js'
import { renderAsync } from '@riotjs/ssr'
renderAsync('async-component', MyComponent, { some: 'initial props' }).then(
(html) => {
console.log(html)
},
)
Notice that the onAsyncRendering
can either return a promise or use the resolve, reject callbacks:
export default {
// this is ok
async onAsyncRendering() {
await loadData()
},
}
export default {
// this is also ok
onAsyncRendering(resolve, reject) {
setTimeout(resolve, 1000)
},
}
IMPORTANT nested onAsyncRendering
on children components are not supported!
You can also extract the rendered html
and css
separately using the fragments
function:
import MyComponent from './my-component.js'
import { fragments } from '@riotjs/ssr'
const { html, css } = fragments('my-component', MyComponent, {
some: 'initial props',
})
It works like the method above but asynchronously
If you want to render your whole document you can simply pass html
as name of your root node. For example
<html>
<head>
<title>{ state.message }</title>
<meta each={ meta in state.meta } {...meta}/>
</head>
<body>
<p>{ state.message }</p>
<script src='path/to/a/script.js'></script>
</body>
<script>
export default {
state: {
message: 'hello',
meta: [{
name: 'description',
content: 'a description'
}]
}
}
</script>
</html>
It can be rendered as it follows:
import MyRootApplication from './my-root-application.js'
import render from '@riotjs/ssr'
const html = render('html', MyRootApplication)
For a better control over your HTML rendering you might want to use the createRenderer
factory function.
This method allows the creation of a rendering function receiving the {getHTML, css, dispose, element}
option object.
getHTML
: give you the rendered html of your component as stringcss
: the css of your component as stringdispose
: clean the memory used on the server needed to render your componentelement
: the component instance you are mounting
For example
import MyComponent from './my-component.js'
import { createRenderer } from '@riotjs/ssr'
const logRendrer = createRenderer(({ getHTML, getCSS, dispose, component }) => {
const html = getHTML()
const css = getCSS()
console.log('Rendering the component: %s', component.name)
dispose()
return { html, css }
})
// use your logRenderer
const { html, css } = logRendrer('my-component', MyComponent, {
some: 'initial props',
})
@riotjs/ssr
needs DOM globals (like window
, document
...) to properly render your markup.
With the domGlobals
exported object you can decide manually when the globals should be created and deleted from in your node applications.
import { domGlobals } from '@riotjs/ssr'
domGlobals.create()
// global DOM object in your node environement are now defined
console.log(global.window, global.document)
// they will be cleared and will be undefined
domGlobals.clear()
If you are rendering your whole HTML you will not be able to use multiple times the inline <script>
<style>
tags.
Of course, you can use only once the ones used by Riot.js to customize your components. For example:
<html>
<head>
<!-- allowed -->
<script src='path/to/some/script.js'></script>
<!-- not allowed -->
<style>
</style>
<!-- not allowed -->
<script>
const globalstuff = {}
</script>
</head>
<body>
<!-- application html -->
</body>
<!-- allowed -->
<script>
export default {
// app code
}
</script>
<!-- allowed -->
<style>
:host {}
</style>
</html>