PillowUser Guide


Something for your web app to get its teeth into!

Pillow is a library for integrating efanXtra components with the BedSheet web framework. It automatically routes web requests to pages and returns the rendered response.


Install afPillow with the Fantom Respository Manager:

C:\> fanr install -r http://repo.status302.com/fanr/ afPillow

Or download the pod from Status302 and copy it to %FAN_HOME%/lib/fan/.

To use in a Fantom project, add a dependency to its build.fan:

depends = ["sys 1.0", ..., "afPillow 0+"]

Quick Start


Hello Mum! I'm <%= age %> years old!


using afIoc
using afEfanXtra
using afPillow

// ---- The only class you need! ----

const mixin Example : Page {
  abstract Int age

  Void initRender(Int age) {
    this.age = age

// ---- Standard BedSheet Support Classes ----

class Main {
  Int main() {
    afBedSheet::Main().main([AppModule#.qname, "8069"])

// SubModule only needed when running from a script
@SubModule { modules=[EfanXtraModule#, PillowModule#] }
class AppModule {

   // Contribution needed when running from a script
   @Contribute { serviceType=EfanTemplateDirectories# }
   static Void contributeEfanDirs(OrderedConfig config) {

      // Look for Example.efan in the same dir as this file

Run the Example.fan script from the command line:

C:\> fan Example.fan

Efan Library: 'app' has 1 page(s):
  Example : /example

Then point your browser at http://localhost:8069/example/42:

Hello Mum! I'm 42 years old!


To create a web page, define a const mixin that extends Page. Example:

using afPillow

const mixin Admin : Page {

Pages are efanXtra components and behave in exactly the same way.

Pillow will automatically route uris with your page name, to your page. Camel casing class names results in a / delimiter. Examples:

`/admin`        --> Admin.fan
`/admin/secret` --> AdminSecret.fan

As seen in the Quick Start example, uris are automatically mapped to the @InitRender methods of your Pages, with uri segments mapped to @InitRender parameters. If the @InitRender method returns a BedSheet response, page rendering is aborted and the response object is processed instead. Example:

using afBedSheet
using afPillow

const mixin Example : Page {

  Obj? initRender(Int age) {
    if (age > 69)
      return Redirect.movedTemporarily(`/too-old`)

    return null

Welcome Pages

Pillow supports the routing of welcome pages, also known as directory pages.

If Pillow recieves a directory uri (a uri that ends in /slash/) then it renders a welcome page, which defaults to Index. Examples:

`/`        --> Index.fan
`/admin/`  --> AdminIndex.fan

More can be read about directory urls in the article: Should Your URLs Point to the Directory or the Index Page?

Release Notes


  • New: Page uris and BedSheet routes are generated from the @InitRender method signature.
  • New: Directory uris may now serve welcome pages.
  • Chg: Updated to use BedSheet 1.2.
  • Chg: Renamed project to afPillow (from afBedSheetEfanExtra).
  • Chg: Reanmed EfanPageMeta to RenderPageMeta.
  • Chg: Renamed PageRoute to PageUri.


  • New: Added @PageRoute facet that lets you specify a bespoke uri
  • New: Added EfanPageMeta which returns the active rendering page.


  • New: Preview Release