Deku logo

Advanced usage

Building and deploying

Deku for fun and profit (but mostly profit)

One way to make money with Deku is to ask all your developer friends to send you a dollar in the mail when they use Deku. Then, they can recruit their friends, who will send them a dollar and send you a nominal commission. And so forth and so on.

Another way is building and deploying apps. This section focuses on the latter.


Single Page Apps

Most PureScript apps export their main function as main from a file called Main.purs. This is then imported into an index.js file, which is the entry point for a single-page app (SPA).

Deku is no different. Take the following Main.purs.

module Main where

import Prelude
import Deku.Toplevel (runInBody)
import Effect (Effect)
import ExampleAssitant (ExampleSignature)
import Deku.Control (text_)
import Deku.Toplevel (runInBody)

main :: Effect Unit
main = void $ runInBody (text_ "I’m main!")

You would then define your index.js thusly:

import { main } from "./output/Main/";
main();

From here, you can use any old bundler, like vite or esbuild to bundle the index into a simple html file and deploy to your hosting service of choice.


SSG, SSR and Vercel

Deku can also be deployed as server-side generated html or server-side rendered app. The easiest way to do this is to use vike for your build and vercel for your deploy.

To render your page as HTML, you can use ssrInBody from Deku.Toplevel.

module Main where

import Prelude
import Deku.Toplevel (runInBody)
import Effect (Effect)
import ExampleAssitant (ExampleSignature)
import Deku.Control (text_)
import Deku.Toplevel (ssrInBody)

main :: Effect Unit
main = void $ ssrInBody (text_ "I’m main!")

This needs a server-side DOM polyfill like jsdom to work. The docs you’re reading are built this way. Check out the source code for this page and be bedazzled by the magic of its pre-rendered HTML.

If you’re just building a static site, you can use ssrInBody and ignore the return value. However, most sites, including this one, have interactive bits. In that case, stash the output of ssrInBody and feed it to hydrateInBody as the first argument.


module MyAwesomeApp where

myAwesomeApp = text_ "I’m awesome!"

-- after which one does ...

module SSR where

import Prelude
import Deku.Toplevel (runInBody)
import Effect (Effect)
import ExampleAssitant (ExampleSignature)
import Deku.Control (text_)
import MyAwesomeApp (myAwesomeApp)
import Deku.Toplevel (ssrInBody, SSROutput)

ssr :: Effect SSROutput
ssr = ssrInBody myAwesomeApp

-- followed by ...

module Hydrate where

import Prelude
import Deku.Toplevel (runInBody)
import Effect (Effect)
import ExampleAssitant (ExampleSignature)
import Deku.Control (text_)
import MyAwesomeApp (myAwesomeApp)
import Deku.Toplevel (hydrateInBody, SSROutput)

hydrate :: SSROutput -> Effect Unit
hydrate cache = hydrateInBody cache myAwesomeApp

After the initial argument to hydrateInBody, the signature is identical to the veritable workhorse runInBody that you've been using all along.

Render the right app

If you render an app using ssrInBody and then hydrate a different app using hydrateInBody, I can't even begin to describe the ill fate that will befall you. Even writing about it makes one's fingers ache. Don't do it. Hydrate the same app you rendered!

For a no-frills example of SSR, check out vike-deku-minimal. For an example that uses client-side routing like these docs, check out vike-deku-client-routing. In both of these examples, SSROutput is serialized to and from JSON.