Skip to main content
The @effect/platform-browser package provides browser-specific implementations for the abstractions defined in @effect/platform, enabling you to build Effect applications that run in web browsers.

Installation

npm install @effect/platform-browser

Available Modules

HTTP

BrowserHttpClient

Make HTTP requests using the Fetch API:
import { BrowserHttpClient } from "@effect/platform-browser"
import { HttpClient } from "@effect/platform"
import * as Effect from "effect/Effect"

const fetchUser = (id: string) =>
  HttpClient.get(`https://api.example.com/users/${id}`).pipe(
    Effect.flatMap((_) => _.json),
    Effect.scoped,
    Effect.provide(BrowserHttpClient.layer)
  )

Effect.runPromise(fetchUser("123")).then(console.log)

Storage

BrowserKeyValueStore

Access browser storage (localStorage/sessionStorage) with Effect:
import { BrowserKeyValueStore } from "@effect/platform-browser"
import { KeyValueStore } from "@effect/platform"
import * as Effect from "effect/Effect"

const program = Effect.gen(function* () {
  const store = yield* KeyValueStore
  
  // Store data
  yield* store.set("user", JSON.stringify({ name: "Alice" }))
  
  // Retrieve data
  const user = yield* store.get("user")
  return user ? JSON.parse(user) : null
}).pipe(
  Effect.provide(BrowserKeyValueStore.layerLocalStorage)
)

// Or use sessionStorage
const sessionStore = BrowserKeyValueStore.layerSessionStorage

WebSockets

BrowserSocket

Create WebSocket connections:
import { BrowserSocket } from "@effect/platform-browser"
import * as Effect from "effect/Effect"
import * as Stream from "effect/Stream"

const socket = BrowserSocket.makeWebSocket("wss://example.com/socket")

const program = Effect.gen(function* () {
  const ws = yield* socket
  
  // Send messages
  yield* ws.send("Hello, server!")
  
  // Receive messages as a stream
  yield* ws.messages.pipe(
    Stream.runForEach((message) => 
      Effect.sync(() => console.log("Received:", message))
    )
  )
})

Workers

BrowserWorker

Use Web Workers with Effect:
import { BrowserWorker } from "@effect/platform-browser"
import { Worker } from "@effect/platform"
import * as Effect from "effect/Effect"

const pool = BrowserWorker.layerManager({
  size: 2,
  spawn: () => new Worker(new URL("./worker.ts", import.meta.url))
})

const program = Effect.gen(function* () {
  const manager = yield* Worker.WorkerManager
  const result = yield* manager.execute("heavy-computation", { data: [1, 2, 3] })
  return result
}).pipe(
  Effect.provide(pool)
)

Browser APIs

Clipboard

Access the Clipboard API:
import { Clipboard } from "@effect/platform-browser"
import * as Effect from "effect/Effect"

const copyText = (text: string) =>
  Effect.gen(function* () {
    const clipboard = yield* Clipboard
    yield* clipboard.writeText(text)
  }).pipe(
    Effect.provide(Clipboard.layer)
  )

const pasteText = Effect.gen(function* () {
  const clipboard = yield* Clipboard
  const text = yield* clipboard.readText()
  return text
}).pipe(
  Effect.provide(Clipboard.layer)
)

Geolocation

Access geolocation data:
import { Geolocation } from "@effect/platform-browser"
import * as Effect from "effect/Effect"
import * as Stream from "effect/Stream"

const getCurrentPosition = Effect.gen(function* () {
  const geo = yield* Geolocation
  const position = yield* geo.getCurrentPosition()
  return {
    lat: position.coords.latitude,
    lng: position.coords.longitude
  }
}).pipe(
  Effect.provide(Geolocation.layer)
)

// Watch position changes
const watchPosition = Effect.gen(function* () {
  const geo = yield* Geolocation
  return geo.watchPosition().pipe(
    Stream.map((pos) => ({
      lat: pos.coords.latitude,
      lng: pos.coords.longitude
    }))
  )
}).pipe(
  Effect.provide(Geolocation.layer)
)

Permissions

Query and request browser permissions:
import { Permissions } from "@effect/platform-browser"
import * as Effect from "effect/Effect"

const checkNotificationPermission = Effect.gen(function* () {
  const permissions = yield* Permissions
  const status = yield* permissions.query({ name: "notifications" })
  
  if (status.state === "prompt") {
    yield* permissions.request({ name: "notifications" })
  }
  
  return status.state
}).pipe(
  Effect.provide(Permissions.layer)
)

Streams

BrowserStream

Work with browser streams (ReadableStream, WritableStream):
import { BrowserStream } from "@effect/platform-browser"
import * as Stream from "effect/Stream"
import * as Effect from "effect/Effect"

const processStream = (readable: ReadableStream<Uint8Array>) => {
  const effectStream = BrowserStream.fromReadableStream(
    () => readable,
    (error) => new Error(String(error))
  )
  
  return effectStream.pipe(
    Stream.runForEach((chunk) => 
      Effect.sync(() => console.log("Chunk:", chunk))
    )
  )
}

Complete Runtime

BrowserRuntime

The BrowserRuntime provides all browser services in a single layer:
import { BrowserRuntime } from "@effect/platform-browser"
import * as Effect from "effect/Effect"

const app = Effect.gen(function* () {
  // All browser services are available
  const http = yield* HttpClient
  const store = yield* KeyValueStore
  const clipboard = yield* Clipboard
  
  // Build your application logic
})

Effect.runPromise(
  app.pipe(
    Effect.provide(BrowserRuntime.layer)
  )
)

Usage in React/Vue/etc.

You can integrate Effect browser services with frontend frameworks:
import { useEffect, useState } from "react"
import { BrowserHttpClient } from "@effect/platform-browser"
import { HttpClient } from "@effect/platform"
import * as Effect from "effect/Effect"

function UserProfile({ userId }: { userId: string }) {
  const [user, setUser] = useState(null)
  
  useEffect(() => {
    const program = HttpClient.get(`/api/users/${userId}`).pipe(
      Effect.flatMap((_) => _.json),
      Effect.scoped,
      Effect.provide(BrowserHttpClient.layer)
    )
    
    Effect.runPromise(program).then(setUser)
  }, [userId])
  
  return <div>{user?.name}</div>
}

Requirements

  • Modern browser with ES2020+ support
  • Effect 4.0.0 or higher

Next Steps

Build docs developers (and LLMs) love