All Posts
Engineering Philosophy1 min read

Functional Reactive Layouts

Why thinking about UI as a function of state — not a collection of mutations — leads to dramatically more maintainable interfaces.

The shift from imperative DOM mutation to declarative state functions is more than a syntax preference. It is a change in epistemology: from how do I change the screen to what should the screen look like given this state.

The Core Principle

In functional reactive programming, the UI is a pure function of application state:

UI=f(State)\text{UI} = f(\text{State})

This is deceptively simple. It means that given identical state, you always get identical UI. No hidden variables, no side-channel mutations, no callbacks that fire in unexpected order.

Why This Matters at Scale

When a UI is a pure function of state, debugging collapses to a single question: what is the state right now? You can serialize it, replay it, and share it. Bugs become reproducible by definition.

// Imperative — UI depends on mutation history
let isLoading = false
let data = null
 
button.addEventListener('click', async () => {
  isLoading = true
  button.disabled = true
  data = await fetchData()
  isLoading = false
  button.disabled = false
  renderData(data)
})
 
// Declarative — UI is a function of state
type State = { status: 'idle' | 'loading' | 'done'; data: Data | null }
 
function View({ state }: { state: State }) {
  return (
    <button disabled={state.status === 'loading'}>
      {state.status === 'loading' ? 'Loading...' : 'Fetch'}
    </button>
  )
}

The Tradeoffs

The functional model has costs. State must be made explicit, which initially feels verbose. But this verbosity is actually signal: you are surfacing complexity that was previously hidden in mutation sequences.

The engineering philosophy I have arrived at: explicit state is always cheaper than implicit history.