On this pageInit
Init & Flags
The counter works, but every time the user refreshes the page, the count resets to zero. What if we want to remember the last count? That’s where Init comes in — and where flags let you pass data into your app at startup.
In the restaurant analogy, init is the waiter’s notebook at the start of the shift — the state of every table before the first customer walks in.
The init function returns the initial Model and any Commands to run on startup. It returns a tuple of [Model, ReadonlyArray<Command<Message>>].
import { Schema as S } from 'effect'
import type { Runtime } from 'foldkit'
import { m } from 'foldkit/message'
const Model = S.Struct({
count: S.Number,
})
type Model = typeof Model.Type
const ClickedIncrement = m('ClickedIncrement')
const ClickedDecrement = m('ClickedDecrement')
const Message = S.Union(ClickedIncrement, ClickedDecrement)
type Message = typeof Message.Type
const init: Runtime.ElementInit<Model, Message> = () => [
Model({ count: 0 }),
[],
]For elements (components without routing), init takes no arguments. For applications with routing, init receives the current URL so you can set up initial state based on the route.
In the restaurant analogy, flags are what the manager tells the waiter before the shift: “table 5 has a reservation at 7, and we’re out of the salmon.” Information from outside the app that shapes the initial state.
Flags let you pass initialization data into your application — things like persisted state from localStorage or configuration values. Define a Flags schema and provide an Effect that loads the flags.
import { KeyValueStore } from '@effect/platform'
import { BrowserKeyValueStore } from '@effect/platform-browser'
import { Effect, Option, Schema as S } from 'effect'
const Todo = S.Struct({
id: S.String,
text: S.String,
completed: S.Boolean,
})
const Todos = S.Array(Todo)
const Flags = S.Struct({
todos: S.Option(Todos),
})
type Flags = typeof Flags.Type
const flags: Effect.Effect<Flags> = Effect.gen(function* () {
const store = yield* KeyValueStore.KeyValueStore
const maybeTodosJson = yield* store.get('todos')
const todosJson = yield* maybeTodosJson
const decodeTodos = S.decode(S.parseJson(Todos))
const todos = yield* decodeTodos(todosJson)
return { todos: Option.some(todos) }
}).pipe(
Effect.catchAll(() => Effect.succeed({ todos: Option.none() })),
Effect.provide(BrowserKeyValueStore.layerLocalStorage),
)When using flags, your init function receives them as the first argument:
import { Option, Schema as S } from 'effect'
import type { Runtime } from 'foldkit'
import { m } from 'foldkit/message'
const Model = S.Struct({
count: S.Number,
startingCount: S.Option(S.Number),
})
type Model = typeof Model.Type
const Flags = S.Struct({
savedCount: S.Option(S.Number),
})
type Flags = typeof Flags.Type
const ClickedIncrement = m('ClickedIncrement')
const Message = S.Union(ClickedIncrement)
type Message = typeof Message.Type
const init: Runtime.ElementInit<Model, Message, Flags> = flags => [
{
count: Option.getOrElse(flags.savedCount, () => 0),
startingCount: flags.savedCount,
},
[],
]You’ve now seen the full Foldkit architecture: the Model holds your state, Messages describe events, update transitions state, the view renders it, Commands handle one-off side effects, Subscriptions manage ongoing streams, and init bootstraps everything. The remaining pages cover utilities, runtime configuration, and advanced topics you’ll reach for as your app grows.