Skip to main content

Layouts

Overview

A layout is UI that is shared between multiple routes. On navigation, layouts preserve state and remain interactive.

Adding layouts

You can create a layout by running the following command:

dotnet elmish-land add layout "/User"
warning

You need to manually add the new layout to your project file by using an IDE or by adding the following line to an ItemGroup in the project file ./MyProject.fsproj:

<Compile Include="src/Pages/User/Layout.fs" />

The "add layout" command generates src/Pages/User/Layout.fs with the following content:

module MyProject.Pages.User.Layout

open Feliz
open ElmishLand
open MyProject.Shared

type Props = unit

type Model = unit

type Msg = | NoOp

let init () =
(),
Command.none

let update (msg: Msg) (model: Model) =
match msg with
| NoOp -> model, Command.none

let routeChanged (model: Model) =
model, Command.none

let view (_model: Model) (content: Feliz.ReactElement) (_dispatch: Msg -> unit) =
Html.div [
Html.text "User Layout"
Html.div [
content
]
]

let layout (_props: Props) (_route: Route) (_shared: SharedModel) =
Layout.from init update routeChanged view

Layouts have the same structure as pages except for the Props type and the layout and routeChanged functions.

Understanding layouts

layout

The layout function is our starting point for the layout. From this function we need to call the Layout.from function to setup our layout.

let layout (_props: Props) (_route: Route) (_shared: SharedModel) =
Layout.from init update routeChanged view

Props

The Props type is used to enable pages to pass initial data to a layout. The Props is passed to the layout function so it's available to init, update, routeChanged and view.

type Props = unit

routeChanged

The routeChanged function is called every time the user navigates with commands or links. This function is NOT called the first time the layout loads. Use the init function for this.

let routeChanged (model: Model) =
model, Command.none

If you need to access the current route from this function, you can pass it from the layout function.

let routeChanged (route: Route) (model: Model) =
model, Command.none

let layout (_props: Props) (route: Route) (_shared: SharedModel) =
Layout.from init update (routeChanged route) view

Layout selection

All pages that are in a sub-folder of a layout will use that layout.

In the following example :

MyProject
└── src
└── Pages
├── Layout.fs
└── User
└── Page.fs

the page /src/Pages/User/Page.fs will use the layout /src/Pages/Layout.fs.

Root Layout

The root layout is defined at the top level of the /src/Pages folder and is automatically created when you initialize a new Elmish Land project.

Sending messages to and from pages

Sending messages to pages

When you need to send messages from a layout to it's current page you will use the Command.ofMsg function from the layout and handle the LayoutMsg on the page. The message will be sent both to the layout and the page.

// A Layout.fs file
type Msg =
| LoadUser of string

let routeChanged (_route: Route) (model: Model) =
model, Command.ofMsg (LoadUser "John Doe")

// A Page.fs file
let update (msg: Msg) (model: Model) =
match msg with
| LayoutMsg (Layout.LoadUser name) -> model, Command.none

In the Commands section, you'll learn more about commands.

Sending messages from pages

When you need to send messages from a page to it's layout you will use the Command.ofLayout function.

// A Page.fs file
let update (msg: Msg) (model: Model) =
match msg with
| LayoutMsg _ -> model, Command.none
| SignOut -> model, Command.ofLayout Layout.SignOutClicked

In the Commands section, you'll learn more about commands.