Skip to main content

Command Module

The Command module provides functions for performing side effects in your Elmish Land application. Commands are used to trigger asynchronous operations, send messages, navigate between pages, and communicate between components.

Where is this defined?

The Command module is automatically generated during the build process:

  • Generated Location: .elmish-land/Base/Command.fs (in your project)
  • Generated By: Running elmish-land restore, elmish-land server and elmish-land build commands
  • Namespace: ElmishLand

The Command type is implemented as a discriminated union that wraps Elmish's standard Cmd<'msg> type, adding support for routing messages to Shared state and parent Layouts.

warning

The .elmish-land/Base/Command.fs file is auto-generated and should not be edited directly. Any changes will be overwritten on the next build.

Command.none

Returns a command that does nothing. Use this when your init or update functions doesn't need to perform any side effects.

Signature

Command.none : Command<'msg, 'sharedMsg, 'layoutMsg>

Example

let update msg model =
match msg with
| Increment ->
{ model with Count = model.Count + 1 },
Command.none

Command.ofMsg

Creates a command that dispatches a message immediately after the current update completes.

Signature

Command.ofMsg : 'msg -> Command<'msg, 'sharedMsg, 'layoutMsg>

Parameters

  • msg - The message to dispatch

Example

let init () =
{ Users = []; Loading = true },
Command.ofMsg LoadUsers

Command.batch

Combines multiple commands into a single command that executes all of them.

Signature

Command.batch : Command<'msg, 'sharedMsg, 'layoutMsg> list -> Command<'msg, 'sharedMsg, 'layoutMsg>

Parameters

  • commands - List of commands to execute

Example

let init () =
model,
Command.batch [
Command.ofMsg LoadUsers
Command.ofMsg LoadSettings
Command.ofShared CheckAuthStatus
]

Command.ofPromise

Executes a promise and dispatches a message with the result. Throws an exception if the promise fails.

Signature

Command.ofPromise :
task: ('arg -> JS.Promise<'result>)
-> arg: 'arg
-> ofSuccess: ('result -> 'msg)
-> Command<'msg, 'sharedMsg, 'layoutMsg>

Parameters

  • task - Function that returns a promise
  • arg - Argument to pass to the task function
  • ofSuccess - Function that converts the result into a message

Example

type Msg =
| LoadUsers
| UsersLoaded of User list

let fetchUsers () =
promise {
let! response = Fetch.fetch "/api/users"
return! response.json<User list>()
}

let update msg model =
match msg with
| LoadUsers ->
{ model with Loading = true },
Command.ofPromise fetchUsers () UsersLoaded

| UsersLoaded users ->
{ model with Users = users; Loading = false },
Command.none

Command.tryOfPromise

Executes a promise and dispatches different messages based on success or failure. Does not throw exceptions.

Signature

Command.tryOfPromise :
task: ('arg -> JS.Promise<'result>)
-> arg: 'arg
-> ofSuccess: ('result -> 'msg)
-> ofError: (exn -> 'msg)
-> Command<'msg, 'sharedMsg, 'layoutMsg>

Parameters

  • task - Function that returns a promise
  • arg - Argument to pass to the task function
  • ofSuccess - Function that converts successful results into a message
  • ofError - Function that converts errors into a message

Example

type Msg =
| LoadUsers
| UsersLoaded of User list
| LoadError of exn

let fetchUsers () =
promise {
let! response = Fetch.fetch "/api/users"
return! response.json<User list>()
}

let update msg model =
match msg with
| LoadUsers ->
{ model with Loading = true; Error = None },
Command.tryOfPromise
fetchUsers
()
UsersLoaded
LoadError

| UsersLoaded users ->
{ model with Users = users; Loading = false },
Command.none

| LoadError error ->
{ model with Loading = false; Error = Some error },
Command.none

Command.ofAsync

Executes an F# async workflow and dispatches a message with the result. Throws an exception if the async operation fails.

This function work seamlessly with Fable.Remoting for type-safe server communication and any F# libraries that return Async<'T> types.

Signature

Command.ofAsync :
task: ('arg -> Async<'result>)
-> arg: 'arg
-> ofSuccess: ('result -> 'msg)
-> Command<'msg, 'sharedMsg, 'layoutMsg>

Parameters

  • task - Function that returns an async workflow
  • arg - Argument to pass to the task function
  • ofSuccess - Function that converts the result into a message

Example, Fable.Remoting

// Shared API definition
type IUserApi = {
getUsers: unit -> Async<User list>
}

// Client code
type Msg =
| LoadUsers
| UsersLoaded of User list

let userApi =
Remoting.createApi()
|> Remoting.buildProxy<IUserApi>

let update msg model =
match msg with
| LoadUsers ->
{ model with Loading = true },
Command.ofAsync userApi.getUsers () UsersLoaded

| UsersLoaded users ->
{ model with Users = users; Loading = false },
Command.none

Command.tryOfAsync

Executes an F# async workflow and dispatches different messages based on success or failure. Does not throw exceptions.

This function work seamlessly with Fable.Remoting for type-safe server communication and any F# libraries that return Async<'T> types.

Signature

Command.tryOfAsync :
task: ('arg -> Async<'result>)
-> arg: 'arg
-> ofSuccess: ('result -> 'msg)
-> ofError: (exn -> 'msg)
-> Command<'msg, 'sharedMsg, 'layoutMsg>

Parameters

  • task - Function that returns an async workflow
  • arg - Argument to pass to the task function
  • ofSuccess - Function that converts successful results into a message
  • ofError - Function that converts errors into a message

When to Use

Use Command.tryOfAsync when you need to handle errors explicitly in your application. This is especially useful with:

  • Fable.Remoting for type-safe server communication
  • F# libraries that return Async<'T> types
  • .NET async APIs

Use Command.tryOfPromise when working with JavaScript libraries and browser APIs.

Example, Fable.Remoting with Error Handling

// Shared API definition
type IWeatherApi = {
getWeatherForecast: unit -> Async<WeatherForecast list>
}

// Client code
type Msg =
| LoadWeather
| WeatherLoaded of WeatherForecast list
| LoadError of exn

let weatherApi =
Remoting.createApi()
|> Remoting.buildProxy<IWeatherApi>

let update msg model =
match msg with
| LoadWeather ->
{ model with Loading = true; Error = None },
Command.tryOfAsync
weatherApi.getWeatherForecast
()
WeatherLoaded
LoadError

| WeatherLoaded forecasts ->
{ model with Forecasts = forecasts; Loading = false },
Command.none

| LoadError error ->
{ model with Loading = false; Error = Some error },
Command.none

Command.ofShared

Sends a message to the Shared module, allowing pages and layouts to update shared state.

Signature

Command.ofShared : 'sharedMsg -> Command<'msg, 'sharedMsg, 'layoutMsg>

Parameters

  • sharedMsg - The message to send to the Shared module

Example

// In a Page.fs file
type Msg =
| SignOutClicked
| LayoutMsg of Layout.Msg

let update msg model =
match msg with
| SignOutClicked ->
model,
Command.ofShared SharedMsg.SignOut

| LayoutMsg _ ->
model, Command.none

See Shared State for more details.

Command.ofLayout

Sends a message from a page to its layout.

Signature

Command.ofLayout : 'layoutMsg -> Command<'msg, 'sharedMsg, 'layoutMsg>

Parameters

  • layoutMsg - The message to send to the layout

Example

// In a Page.fs file
type Msg =
| UpdateTitle of string
| LayoutMsg of Layout.Msg

let update msg model =
match msg with
| UpdateTitle title ->
model,
Command.ofLayout (Layout.SetTitle title)

| LayoutMsg _ ->
model, Command.none

See Layouts - Sending messages for more details.

Command.navigate

Navigates to a different page in your application.

Signature

Command.navigate : Route -> Command<'msg, 'sharedMsg, 'layoutMsg>

Parameters

  • route - The route to navigate to (from the generated Route discriminated union)

Example (Static Route)

type Msg =
| LoginSuccessful of User
| LayoutMsg of Layout.Msg

let update msg model =
match msg with
| LoginSuccessful user ->
model,
Command.navigate (Route.Dashboard ())

| LayoutMsg _ ->
model, Command.none

Example (Dynamic Route)

Navigate to routes with parameters:

// Navigate to /#users/123
Command.navigate (Route.UsersId { Id = "123"; Query = [] })

// Navigate to /#blog/my-post?highlight=true
Command.navigate (
Route.BlogSlug {
Slug = "my-post"
Query = [ "highlight", "true" ]
}
)

See Linking and Navigating for more details.

Command.ofCmd

Converts an Elmish Cmd<'msg> to an Elmish Land Command<'msg, 'sharedMsg, 'layoutMsg>. This is useful when integrating with libraries that return standard Elmish commands.

Signature

Command.ofCmd : Cmd<'msg> -> Command<'msg, 'sharedMsg, 'layoutMsg>

Parameters

  • cmd - The Elmish command to convert

Example

open Elmish

let update msg model =
match msg with
| UseElmishLibrary ->
let elmishCmd = SomeLibrary.createCommand ()
model,
Command.ofCmd elmishCmd

Best Practices

Prefer tryOfPromise/tryOfAsync over ofPromise/ofAsync

Always use Command.tryOfPromise or Command.tryOfAsync instead of their non-try counterparts in production code to handle errors gracefully:

// Good - Handles errors (Promise-based)
Command.tryOfPromise fetchData () DataLoaded LoadError

// Good - Handles errors (Async-based)
Command.tryOfAsync fetchData () DataLoaded LoadError

// Avoid - Errors will crash the application
Command.ofPromise fetchData () DataLoaded
Command.ofAsync fetchData () DataLoaded

When you need to perform multiple side effects, use Command.batch to keep your code organized:

let init () =
model,
Command.batch [
Command.tryOfAsync loadUser () UserLoaded LoadError
Command.tryOfAsync loadSettings () SettingsLoaded LoadError
Command.ofShared CheckAuthStatus
]

Use Specific Message Types

Create specific message types for different command results rather than reusing generic ones:

// Good
type Msg =
| UsersLoaded of User list
| SettingsLoaded of Settings
| ProfileLoaded of Profile

// Avoid
type Msg =
| DataLoaded of obj

This improves type safety and makes your code more maintainable.