Layout Module
The Layout module provides functions for creating and configuring layouts in Elmish Land. Layouts are UI components that wrap pages and preserve state during navigation.
Where is this defined?
The Layout module is automatically generated during the build process:
- Generated Location:
.elmish-land/Base/Layout.fs(in your project) - Generated By: Running
elmish-land restore,elmish-land serverandelmish-land buildcommands - Namespace:
ElmishLand
warning
The .elmish-land/Base/Layout.fs file is auto-generated and should not be edited directly. Any changes will be overwritten on the next build.
Layout.from
Creates a layout with init, update, routeChanged, and view functions.
Signature
Layout.from :
init: (unit -> 'layoutModel * Command<'layoutMsg, 'sharedMsg, 'layoutMsg>)
-> update: ('layoutMsg -> 'layoutModel -> 'layoutModel * Command<'layoutMsg, 'sharedMsg, 'layoutMsg>)
-> routeChanged: ('layoutModel -> 'layoutModel * Command<'layoutMsg, 'sharedMsg, 'layoutMsg>)
-> view: ('layoutModel -> ReactElement -> ('layoutMsg -> unit) -> ReactElement)
-> Layout<'sharedMsg, 'layoutModel, 'layoutMsg>
Parameters
init- Initialization function that returns the initial model and command. Called once when the layout first loads.update- Message handler that receives a message and current model, returning updated model and command.routeChanged- Function called on every navigation except initial load. Receives current model, returns updated model and command.view- Rendering function that receives the current model, page content to wrap, and dispatch function. Returns the layout's ReactElement.
Example
module MyProject.Pages.User.Layout
open Feliz
open ElmishLand
open MyProject.Shared
type Props = unit
type Model = {
IsMenuOpen: bool
}
type Msg =
| ToggleMenu
| CloseMenu
let init () =
{ IsMenuOpen = false },
Command.none
let update (msg: Msg) (model: Model) =
match msg with
| ToggleMenu ->
{ model with IsMenuOpen = not model.IsMenuOpen },
Command.none
| CloseMenu ->
{ model with IsMenuOpen = false },
Command.none
let routeChanged (model: Model) =
// Close menu on navigation
{ model with IsMenuOpen = false },
Command.none
let view (model: Model) (content: ReactElement) (dispatch: Msg -> unit) =
Html.div [
Html.nav [
Html.button [
prop.onClick (fun _ -> dispatch ToggleMenu)
prop.text "Menu"
]
if model.IsMenuOpen then
Html.div [
Html.text "User Menu Items"
]
]
Html.main [
content
]
]
let layout (_props: Props) (_route: Route) (_shared: SharedModel) =
Layout.from init update routeChanged view
Layout.withSubscriptions
Adds subscriptions to a layout, allowing it to listen to external events like timers or WebSocket connections.
Signature
Layout.withSubscriptions :
subscriptions: ('layoutModel -> (string list * (('layoutMsg -> unit) -> System.IDisposable)) list)
-> layout: Layout<'sharedMsg, 'layoutModel, 'layoutMsg>
-> Layout<'sharedMsg, 'layoutModel, 'layoutMsg>
Parameters
subscriptions- Function that receives the current model and returns a list of subscriptions. Each subscription is a tuple of (ID list, start function).layout- The layout to add subscriptions to.
Example
let onNotificationReceived model dispatch =
// Setup WebSocket listener
let subscription () =
dispatch (NotificationReceived "New message")
let intervalId = Browser.Dom.window.setInterval(subscription, 5000)
React.createDisposable (fun () ->
Browser.Dom.window.clearInterval(intervalId)
)
let subscriptions model =
[
if model.IsAuthenticated then
[ "notifications" ], onNotificationReceived model
]
let layout (_props: Props) (_route: Route) (shared: SharedModel) =
Layout.from init update routeChanged view
|> Layout.withSubscriptions subscriptions
Best Practices
When to Use routeChanged vs init
init: Use for one-time setup when the layout first loads. The layout model persists across navigation, soinitis only called once.routeChanged: Use for actions that should occur on every navigation, like navigating between pages with the same layout.