From Lit

Context

@provide and @consume become ContextProvider and ContextConsumer, the controllers that @lit/context also exports. A context is a symbol branded with the type of its value, so a consumer gets that type back.

Providing and consuming

The provider sets a new value with setValue, where the decorated field is assigned. To let other code set it, as a public @provide field allows, give the F# class a property whose setter calls setValue. The consumer's value is an option: None until a provider answers.

Lit
import { LitElement, html } from 'lit';
import { customElement } from 'lit/decorators.js';
import {
  consume, createContext, provide,
} from '@lit/context';

type Units = 'metric' | 'imperial';
const unitsContext = createContext<Units>(Symbol('units'));

@customElement('units-provider')
export class UnitsProvider extends LitElement {
  @provide({ context: unitsContext })
  private units: Units = 'metric';

  private toggle() {
    this.units =
      this.units === 'metric' ? 'imperial' : 'metric';
  }

  render() {
    return html`
      <button @click=${this.toggle}>Switch units</button>
      <slot></slot>`;
  }
}

@customElement('run-distance')
export class RunDistance extends LitElement {
  @consume({ context: unitsContext, subscribe: true })
  units?: Units;

  render() {
    const label =
      this.units === 'imperial' ? '3.1 mi' : '5 km';
    return html`${label}`;
  }
}
Firelight
open Fable.Core
open Fable.Core.JsInterop
open Firelight
open Firelight.Context
open type Firelight.Lit

type Units =
    | Metric
    | Imperial

type UnitsContext =
    inherit Context<Units>
    inherit symbol

let unitsContext: UnitsContext =
    LitContext.createContext (JS.Symbol "units")

[<AttachMembers>]
type UnitsProvider() =
    inherit LitElement()

    let mutable units = Metric

    let provider =
        ContextProvider(
            jsThis,
            ContextProvider.Options(unitsContext, units)
        )

    member private _.Toggle() =
        units <- if units = Metric then Imperial else Metric
        provider.setValue units

    override this.render() =
        let toggle _ = this.Toggle()

        html $"""
        <button @click={toggle}>Switch units</button>
        <slot></slot>"""

[<AttachMembers>]
type RunDistance() =
    inherit LitElement()

    let units =
        ContextConsumer(
            jsThis,
            ContextConsumer.Options(
                unitsContext, subscribe = true)
        )

    override _.render() =
        let label =
            if units.value = Some Imperial then "3.1 mi"
            else "5 km"

        html $"{label}"

defineElement<UnitsProvider> "units-provider"
defineElement<RunDistance> "run-distance"

The F# context's type lists Context<Units> and symbol, which createContext<Units> does for TypeScript. Both classes take the element as their host, here as jsThis, the JavaScript this of the object being constructed. They also accept the F# this, with as this on the type.

The provider here doesn't show the value itself, so it doesn't call this.requestUpdate(). A provider that does show it needs to, as setValue only updates the consumers.

Without decorators

Lit code that already uses the controllers translates line by line:

Lit Firelight
new ContextProvider(this, { context, initialValue }) ContextProvider(jsThis, ContextProvider.Options(context, initialValue))
provider.setValue(v) provider.setValue v
new ContextConsumer(this, { context, subscribe: true }) ContextConsumer(jsThis, ContextConsumer.Options(context, subscribe = true))
{ context, callback: (v, unsubscribe) => ... } ContextConsumer.Options(context, callback = fun v unsubscribe -> ...)
consumer.value consumer.value, a 'T option
new ContextRoot().attach(document.body) ContextRoot().attach document.body

No direct equivalent

The Firelight.Context page has live examples, including a ContextRoot, and the Todo demo shares its Elmish state and dispatch through context.