or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

Files

docs

browser-integration.mdindex.mdmaterial-design.mdresource-management.mdstate-management.mdui-components.mdwindow-management.md

browser-integration.mddocs/

0

# Browser Integration

1

2

Compose Multiplatform for WASM/JS provides seamless integration with browser APIs and web platform features. This enables access to browser-specific functionality, system preferences, network operations, and JavaScript interoperability while maintaining the declarative Compose programming model.

3

4

## Browser API Access

5

6

### Window and Document

7

8

Direct access to browser window and document objects.

9

10

```kotlin { .api }

11

// Browser window access

12

val window: Window = kotlinx.browser.window

13

val document: Document = kotlinx.browser.document

14

```

15

16

**Window Operations:**

17

```kotlin { .api }

18

@Composable

19

fun WindowIntegration() {

20

LaunchedEffect(Unit) {

21

// Window properties

22

val windowWidth = window.innerWidth

23

val windowHeight = window.innerHeight

24

25

// Browser information

26

val userAgent = window.navigator.userAgent

27

val language = window.navigator.language

28

val platform = window.navigator.platform

29

30

// Location and history

31

val currentUrl = window.location.href

32

window.history.pushState(null, "New Page", "/new-path")

33

}

34

}

35

```

36

37

**Document Manipulation:**

38

```kotlin { .api }

39

@Composable

40

fun DocumentIntegration() {

41

LaunchedEffect(Unit) {

42

// Document properties

43

val title = document.title

44

document.title = "New Title"

45

46

// Element access

47

val element = document.getElementById("myElement")

48

val canvas = document.querySelector("canvas")

49

50

// DOM manipulation

51

val newElement = document.createElement("div")

52

newElement.textContent = "Created from Kotlin"

53

document.body?.appendChild(newElement)

54

}

55

}

56

```

57

58

## System Preferences Detection

59

60

### Dark Mode Detection

61

62

```kotlin { .api }

63

fun isSystemInDarkTheme(): Boolean {

64

return window.matchMedia("(prefers-color-scheme: dark)").matches

65

}

66

67

@Composable

68

fun DarkModeAwareTheme(content: @Composable () -> Unit) {

69

var isDarkMode by remember { mutableStateOf(isSystemInDarkTheme()) }

70

71

LaunchedEffect(Unit) {

72

val mediaQuery = window.matchMedia("(prefers-color-scheme: dark)")

73

val listener: (MediaQueryListEvent) -> Unit = { event ->

74

isDarkMode = event.matches

75

}

76

77

mediaQuery.addEventListener("change", listener)

78

// Cleanup handled automatically

79

}

80

81

MaterialTheme(

82

colors = if (isDarkMode) darkColors() else lightColors(),

83

content = content

84

)

85

}

86

```

87

88

### Language and Locale

89

90

```kotlin { .api }

91

fun getCurrentLanguage(): String {

92

return window.navigator.languages.firstOrNull()

93

?: window.navigator.language

94

?: "en"

95

}

96

97

fun getCurrentLocale(): String {

98

val language = getCurrentLanguage()

99

return language.split("-").first() // Extract primary language

100

}

101

102

@Composable

103

fun LocalizedContent() {

104

val currentLanguage = remember { getCurrentLanguage() }

105

val isRightToLeft = remember {

106

listOf("ar", "he", "fa", "ur").contains(currentLanguage.split("-").first())

107

}

108

109

CompositionLocalProvider(

110

LocalLayoutDirection provides if (isRightToLeft) LayoutDirection.Rtl else LayoutDirection.Ltr

111

) {

112

// Content automatically adapts to language direction

113

Text(stringResource(Res.string.welcome_message))

114

}

115

}

116

```

117

118

### Display and Accessibility

119

120

```kotlin { .api }

121

fun getDevicePixelRatio(): Double {

122

return window.devicePixelRatio

123

}

124

125

fun prefersReducedMotion(): Boolean {

126

return window.matchMedia("(prefers-reduced-motion: reduce)").matches

127

}

128

129

fun prefersHighContrast(): Boolean {

130

return window.matchMedia("(prefers-contrast: high)").matches

131

}

132

133

@Composable

134

fun AccessibilityAwareUI() {

135

val reducedMotion = remember { prefersReducedMotion() }

136

val highContrast = remember { prefersHighContrast() }

137

138

AnimatedVisibility(

139

visible = shouldShowContent,

140

enter = if (reducedMotion) EnterTransition.None else fadeIn(),

141

exit = if (reducedMotion) ExitTransition.None else fadeOut()

142

) {

143

Surface(

144

color = if (highContrast) Color.Black else MaterialTheme.colors.surface

145

) {

146

// Content adapted for accessibility

147

}

148

}

149

}

150

```

151

152

## Network Operations

153

154

### Fetch API

155

156

Modern web API for network requests.

157

158

```kotlin { .api }

159

suspend fun fetchData(url: String): String {

160

return window.fetch(url).await().text().await()

161

}

162

163

suspend fun fetchJson(url: String): dynamic {

164

val response = window.fetch(url).await()

165

if (!response.ok) {

166

throw Exception("Network request failed: ${response.status}")

167

}

168

return response.json().await()

169

}

170

171

@Composable

172

fun NetworkDataLoader() {

173

var data by remember { mutableStateOf<String?>(null) }

174

var isLoading by remember { mutableStateOf(false) }

175

var error by remember { mutableStateOf<String?>(null) }

176

177

LaunchedEffect(Unit) {

178

isLoading = true

179

try {

180

data = fetchData("https://api.example.com/data")

181

error = null

182

} catch (e: Exception) {

183

error = e.message

184

data = null

185

} finally {

186

isLoading = false

187

}

188

}

189

190

when {

191

isLoading -> CircularProgressIndicator()

192

error != null -> Text("Error: $error", color = MaterialTheme.colors.error)

193

data != null -> Text("Data: $data")

194

}

195

}

196

```

197

198

### XMLHttpRequest

199

200

Traditional HTTP requests with more control.

201

202

```kotlin { .api }

203

suspend fun xmlHttpRequest(url: String, method: String = "GET"): String {

204

return suspendCoroutine { continuation ->

205

val xhr = XMLHttpRequest()

206

xhr.open(method, url)

207

208

xhr.onload = {

209

if (xhr.status == 200.toShort()) {

210

continuation.resume(xhr.responseText)

211

} else {

212

continuation.resumeWithException(

213

Exception("HTTP ${xhr.status}: ${xhr.statusText}")

214

)

215

}

216

}

217

218

xhr.onerror = {

219

continuation.resumeWithException(Exception("Network error"))

220

}

221

222

xhr.send()

223

}

224

}

225

```

226

227

## Local Storage

228

229

### Browser Storage APIs

230

231

```kotlin { .api }

232

object BrowserStorage {

233

fun setItem(key: String, value: String) {

234

window.localStorage.setItem(key, value)

235

}

236

237

fun getItem(key: String): String? {

238

return window.localStorage.getItem(key)

239

}

240

241

fun removeItem(key: String) {

242

window.localStorage.removeItem(key)

243

}

244

245

fun clear() {

246

window.localStorage.clear()

247

}

248

}

249

250

@Composable

251

fun PersistentSettings() {

252

var theme by remember {

253

val saved = BrowserStorage.getItem("theme") ?: "light"

254

mutableStateOf(saved)

255

}

256

257

LaunchedEffect(theme) {

258

BrowserStorage.setItem("theme", theme)

259

}

260

261

Switch(

262

checked = theme == "dark",

263

onCheckedChange = { isDark ->

264

theme = if (isDark) "dark" else "light"

265

}

266

)

267

}

268

```

269

270

### Session Storage

271

272

```kotlin { .api }

273

object SessionStorage {

274

fun setItem(key: String, value: String) {

275

window.sessionStorage.setItem(key, value)

276

}

277

278

fun getItem(key: String): String? {

279

return window.sessionStorage.getItem(key)

280

}

281

}

282

```

283

284

## Platform Detection

285

286

### Runtime Platform Information

287

288

```kotlin { .api }

289

fun getCurrentPlatform(): String = "Web WASM"

290

291

fun getBrowserInfo(): BrowserInfo {

292

val userAgent = window.navigator.userAgent

293

return BrowserInfo(

294

name = detectBrowserName(userAgent),

295

version = detectBrowserVersion(userAgent),

296

isMobile = isMobileDevice(userAgent),

297

supportsWebAssembly = supportsWasm(),

298

supportsWebGL = supportsWebGL()

299

)

300

}

301

302

data class BrowserInfo(

303

val name: String,

304

val version: String,

305

val isMobile: Boolean,

306

val supportsWebAssembly: Boolean,

307

val supportsWebGL: Boolean

308

)

309

310

fun detectBrowserName(userAgent: String): String {

311

return when {

312

"Chrome" in userAgent -> "Chrome"

313

"Firefox" in userAgent -> "Firefox"

314

"Safari" in userAgent -> "Safari"

315

"Edge" in userAgent -> "Edge"

316

else -> "Unknown"

317

}

318

}

319

320

@Composable

321

fun PlatformAwareUI() {

322

val browserInfo = remember { getBrowserInfo() }

323

324

if (browserInfo.isMobile) {

325

// Mobile-optimized UI

326

Column(

327

modifier = Modifier.padding(8.dp)

328

) {

329

// Larger touch targets

330

Button(

331

onClick = { /* action */ },

332

modifier = Modifier

333

.fillMaxWidth()

334

.height(56.dp)

335

) {

336

Text("Mobile Button")

337

}

338

}

339

} else {

340

// Desktop-optimized UI

341

Row {

342

// More compact layout

343

Button(onClick = { /* action */ }) {

344

Text("Desktop Button")

345

}

346

}

347

}

348

}

349

```

350

351

## JavaScript Interoperability

352

353

### Calling JavaScript Functions

354

355

```kotlin { .api }

356

// Define external JavaScript functions

357

@JsFun("(message) => { console.log(message); }")

358

external fun jsConsoleLog(message: String)

359

360

@JsFun("(callback) => { setTimeout(callback, 1000); }")

361

external fun jsSetTimeout(callback: () -> Unit)

362

363

@JsFun("() => { return Date.now(); }")

364

external fun jsGetTimestamp(): Double

365

366

@Composable

367

fun JavaScriptIntegration() {

368

LaunchedEffect(Unit) {

369

// Call JavaScript functions

370

jsConsoleLog("Hello from Kotlin!")

371

372

val timestamp = jsGetTimestamp()

373

println("Current timestamp: $timestamp")

374

375

jsSetTimeout {

376

println("JavaScript timeout callback executed")

377

}

378

}

379

}

380

```

381

382

### Accessing JavaScript Objects

383

384

```kotlin { .api }

385

@Composable

386

fun JavaScriptObjectAccess() {

387

LaunchedEffect(Unit) {

388

// Access global JavaScript objects

389

val console = window.asDynamic().console

390

console.log("Direct console access")

391

392

// Use browser APIs

393

val performance = window.asDynamic().performance

394

val navigationStart = performance.timing.navigationStart

395

396

// Access custom JavaScript objects

397

val customObject = window.asDynamic().myCustomObject

398

if (customObject != null) {

399

customObject.customMethod("parameter")

400

}

401

}

402

}

403

```

404

405

## URL and Navigation

406

407

### URL Management

408

409

```kotlin { .api }

410

@Composable

411

fun URLManagement() {

412

var currentPath by remember { mutableStateOf(window.location.pathname) }

413

414

LaunchedEffect(Unit) {

415

// Listen for navigation events

416

window.addEventListener("popstate") { event ->

417

currentPath = window.location.pathname

418

}

419

}

420

421

fun navigateTo(path: String) {

422

window.history.pushState(null, "", path)

423

currentPath = path

424

}

425

426

when (currentPath) {

427

"/" -> HomeScreen()

428

"/about" -> AboutScreen()

429

"/settings" -> SettingsScreen()

430

else -> NotFoundScreen()

431

}

432

433

BottomNavigation {

434

BottomNavigationItem(

435

icon = { Icon(Icons.Default.Home, contentDescription = null) },

436

label = { Text("Home") },

437

selected = currentPath == "/",

438

onClick = { navigateTo("/") }

439

)

440

BottomNavigationItem(

441

icon = { Icon(Icons.Default.Info, contentDescription = null) },

442

label = { Text("About") },

443

selected = currentPath == "/about",

444

onClick = { navigateTo("/about") }

445

)

446

}

447

}

448

```

449

450

### URL Parameters

451

452

```kotlin { .api }

453

fun parseUrlParameters(): Map<String, String> {

454

val params = mutableMapOf<String, String>()

455

val search = window.location.search.removePrefix("?")

456

457

if (search.isNotEmpty()) {

458

search.split("&").forEach { param ->

459

val (key, value) = param.split("=", limit = 2)

460

params[decodeURIComponent(key)] = decodeURIComponent(value)

461

}

462

}

463

464

return params

465

}

466

467

@Composable

468

fun URLParameterHandler() {

469

val urlParams = remember { parseUrlParameters() }

470

val userId = urlParams["userId"]

471

val tab = urlParams["tab"] ?: "profile"

472

473

userId?.let { id ->

474

UserProfile(userId = id, initialTab = tab)

475

} ?: run {

476

Text("User ID not provided")

477

}

478

}

479

```

480

481

## Performance Monitoring

482

483

### Performance API

484

485

```kotlin { .api }

486

fun measurePerformance(): PerformanceData {

487

val performance = window.asDynamic().performance

488

val timing = performance.timing

489

490

return PerformanceData(

491

navigationStart = timing.navigationStart as Double,

492

domContentLoaded = timing.domContentLoadedEventEnd as Double,

493

loadComplete = timing.loadEventEnd as Double,

494

firstPaint = getFirstPaintTime(),

495

memory = getMemoryUsage()

496

)

497

}

498

499

data class PerformanceData(

500

val navigationStart: Double,

501

val domContentLoaded: Double,

502

val loadComplete: Double,

503

val firstPaint: Double?,

504

val memory: MemoryInfo?

505

)

506

507

@Composable

508

fun PerformanceMonitor() {

509

var performanceData by remember { mutableStateOf<PerformanceData?>(null) }

510

511

LaunchedEffect(Unit) {

512

delay(1000) // Wait for page to load

513

performanceData = measurePerformance()

514

}

515

516

performanceData?.let { data ->

517

Column {

518

Text("Load Time: ${data.loadComplete - data.navigationStart}ms")

519

Text("DOM Ready: ${data.domContentLoaded - data.navigationStart}ms")

520

data.memory?.let { memory ->

521

Text("Memory Used: ${memory.usedJSHeapSize / 1024 / 1024}MB")

522

}

523

}

524

}

525

}

526

```

527

528

## Error Handling and Debugging

529

530

### Browser Developer Tools

531

532

```kotlin { .api }

533

fun logToBrowserConsole(message: String, level: LogLevel = LogLevel.LOG) {

534

val console = window.asDynamic().console

535

when (level) {

536

LogLevel.LOG -> console.log(message)

537

LogLevel.INFO -> console.info(message)

538

LogLevel.WARN -> console.warn(message)

539

LogLevel.ERROR -> console.error(message)

540

}

541

}

542

543

enum class LogLevel { LOG, INFO, WARN, ERROR }

544

545

@Composable

546

fun DebuggingSupport() {

547

LaunchedEffect(Unit) {

548

// Enable debugging in development

549

if (isDebugMode()) {

550

logToBrowserConsole("App started in debug mode", LogLevel.INFO)

551

552

// Register global error handler

553

window.addEventListener("error") { event ->

554

val errorEvent = event as ErrorEvent

555

logToBrowserConsole(

556

"Global error: ${errorEvent.message} at ${errorEvent.filename}:${errorEvent.lineno}",

557

LogLevel.ERROR

558

)

559

}

560

}

561

}

562

}

563

564

fun isDebugMode(): Boolean {

565

return window.location.hostname == "localhost" ||

566

window.location.search.contains("debug=true")

567

}

568

```