or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

Files

docs

accessibility.mdanimation.mdcore-utilities.mdform-controls.mdhooks.mdindex.mdinteractive-components.mdlayout-components.mdlist-components.mdmedia-components.mdplatform-apis.mdstylesheet.mdsystem-integration.mdtext-input.md

platform-apis.mddocs/

0

# Platform APIs

1

2

React Native's platform APIs adapted for web environments, including device information, system services, web integrations, and cross-platform utilities.

3

4

## Platform

5

6

Provides platform detection and conditional code execution with web-specific implementations.

7

8

```javascript { .api }

9

const Platform: {

10

OS: 'web';

11

Version: string;

12

isTesting: boolean;

13

select: <T>(specifics: {web?: T, default?: T}) => T;

14

};

15

```

16

17

**Properties:**

18

- `OS` - Always `'web'` for React Native Web

19

- `Version` - Platform version (always `'0.0.0'` for web)

20

- `isTesting` - Whether running in test environment

21

22

**Methods:**

23

- `select(obj)` - Select platform-specific values

24

25

**Usage:**

26

```javascript

27

import { Platform } from "react-native-web";

28

29

// Check platform

30

if (Platform.OS === 'web') {

31

console.log('Running on web');

32

}

33

34

// Platform-specific values

35

const styles = StyleSheet.create({

36

container: {

37

flex: 1,

38

paddingTop: Platform.select({

39

web: 20,

40

default: 0

41

})

42

},

43

text: {

44

fontSize: Platform.select({

45

web: 16,

46

default: 14

47

})

48

}

49

});

50

51

// Platform-specific components

52

const HeaderComponent = Platform.select({

53

web: WebHeader,

54

default: DefaultHeader

55

});

56

57

// Conditional rendering

58

function App() {

59

return (

60

<View>

61

<Text>Hello React Native Web!</Text>

62

{Platform.OS === 'web' && (

63

<Text>This only shows on web</Text>

64

)}

65

</View>

66

);

67

}

68

69

// Environment detection

70

const isProduction = !Platform.isTesting && process.env.NODE_ENV === 'production';

71

```

72

73

## StatusBar

74

75

Component and API for controlling the status bar appearance and behavior. On web, this is a compatibility component that provides no-op implementations for mobile-specific status bar functionality.

76

77

```javascript { .api }

78

const StatusBar: React.ComponentType<StatusBarProps> & {

79

setBackgroundColor: (color: string, animated?: boolean) => void;

80

setBarStyle: (style: 'default' | 'light-content' | 'dark-content', animated?: boolean) => void;

81

setHidden: (hidden: boolean, animation?: 'none' | 'fade' | 'slide') => void;

82

setNetworkActivityIndicatorVisible: (visible: boolean) => void;

83

setTranslucent: (translucent: boolean) => void;

84

};

85

```

86

87

**Web Implementation:**

88

StatusBar on web is a compatibility layer that renders null and provides no-op static methods. This ensures cross-platform compatibility for apps that use StatusBar on mobile platforms.

89

90

### Component Usage

91

92

```javascript { .api }

93

<StatusBar barStyle="dark-content" backgroundColor="#ffffff" hidden={false} />

94

```

95

96

### Static Methods

97

98

```javascript { .api }

99

// All static methods are no-ops on web but maintain compatibility

100

StatusBar.setBackgroundColor(color: string, animated?: boolean): void

101

StatusBar.setBarStyle(style: 'default' | 'light-content' | 'dark-content', animated?: boolean): void

102

StatusBar.setHidden(hidden: boolean, animation?: 'none' | 'fade' | 'slide'): void

103

StatusBar.setNetworkActivityIndicatorVisible(visible: boolean): void

104

StatusBar.setTranslucent(translucent: boolean): void

105

```

106

107

**Usage:**

108

```javascript

109

import React from "react";

110

import { StatusBar, View, Text } from "react-native-web";

111

112

function App() {

113

return (

114

<View style={{ flex: 1 }}>

115

{/* StatusBar component renders nothing on web but maintains compatibility */}

116

<StatusBar

117

barStyle="dark-content"

118

backgroundColor="#ffffff"

119

hidden={false}

120

/>

121

122

<Text>App content here</Text>

123

</View>

124

);

125

}

126

127

// Static methods are no-ops on web

128

function setStatusBarStyle() {

129

// These calls do nothing on web but don't throw errors

130

StatusBar.setBarStyle('light-content');

131

StatusBar.setBackgroundColor('#000000');

132

StatusBar.setHidden(true, 'fade');

133

}

134

```

135

136

**Cross-Platform Considerations:**

137

```javascript

138

import { Platform, StatusBar } from "react-native-web";

139

140

function ConfigureStatusBar() {

141

// Only configure status bar on mobile platforms

142

if (Platform.OS !== 'web') {

143

StatusBar.setBarStyle('light-content');

144

StatusBar.setBackgroundColor('#1a1a1a');

145

}

146

147

// Or use the component approach (safe on all platforms)

148

return (

149

<StatusBar

150

barStyle="light-content"

151

backgroundColor="#1a1a1a"

152

/>

153

);

154

}

155

```

156

157

## Dimensions

158

159

Utilities for accessing screen and window dimensions with responsive design support and automatic updates.

160

161

```javascript { .api }

162

const Dimensions: {

163

get: (dimension: 'window' | 'screen') => DisplayMetrics;

164

addEventListener: (type: 'change', handler: (dimensions: DimensionsValue) => void) => EventSubscription;

165

removeEventListener: (type: 'change', handler: Function) => void;

166

set: (initialDimensions?: DimensionsValue) => void;

167

};

168

```

169

170

**DisplayMetrics:**

171

```javascript

172

{

173

width: number; // Width in CSS pixels

174

height: number; // Height in CSS pixels

175

scale: number; // Device pixel ratio

176

fontScale: number; // Font scale factor (always 1 on web)

177

}

178

```

179

180

**Dimensions:**

181

- `window` - Viewport dimensions (changes with browser resize)

182

- `screen` - Screen dimensions (full screen size)

183

184

**Usage:**

185

```javascript

186

import { Dimensions } from "react-native-web";

187

188

// Get dimensions

189

const windowDimensions = Dimensions.get('window');

190

const screenDimensions = Dimensions.get('screen');

191

192

console.log('Window:', windowDimensions);

193

// { width: 1024, height: 768, scale: 2, fontScale: 1 }

194

195

console.log('Screen:', screenDimensions);

196

// { width: 1920, height: 1080, scale: 2, fontScale: 1 }

197

198

// Responsive design

199

function ResponsiveComponent() {

200

const [dimensions, setDimensions] = useState(Dimensions.get('window'));

201

202

useEffect(() => {

203

const subscription = Dimensions.addEventListener('change', ({ window }) => {

204

setDimensions(window);

205

});

206

207

return () => subscription?.remove();

208

}, []);

209

210

const isTablet = dimensions.width >= 768;

211

const isDesktop = dimensions.width >= 1024;

212

213

return (

214

<View style={[

215

styles.container,

216

isTablet && styles.tablet,

217

isDesktop && styles.desktop

218

]}>

219

<Text>Current width: {dimensions.width}px</Text>

220

<Text>Device type: {isDesktop ? 'Desktop' : isTablet ? 'Tablet' : 'Mobile'}</Text>

221

</View>

222

);

223

}

224

225

// Breakpoint utilities

226

const breakpoints = {

227

mobile: 0,

228

tablet: 768,

229

desktop: 1024,

230

wide: 1200

231

};

232

233

function getBreakpoint(width) {

234

if (width >= breakpoints.wide) return 'wide';

235

if (width >= breakpoints.desktop) return 'desktop';

236

if (width >= breakpoints.tablet) return 'tablet';

237

return 'mobile';

238

}

239

240

// Layout calculations

241

function calculateLayout() {

242

const { width, height } = Dimensions.get('window');

243

const isLandscape = width > height;

244

const aspectRatio = width / height;

245

246

return {

247

isLandscape,

248

aspectRatio,

249

gridColumns: Math.floor(width / 300), // 300px per column

250

itemWidth: (width - 40) / 2 // 20px margin, 2 columns

251

};

252

}

253

```

254

255

## AppState

256

257

Application state management for detecting when the app becomes active, inactive, or goes to background using the Page Visibility API.

258

259

```javascript { .api }

260

const AppState: {

261

currentState: 'active' | 'background';

262

isAvailable: boolean;

263

addEventListener: (type: 'change' | 'memoryWarning', handler: (state: string) => void) => EventSubscription;

264

};

265

```

266

267

**States:**

268

- `active` - App is in foreground and visible

269

- `background` - App is in background (page is hidden)

270

271

**Usage:**

272

```javascript

273

import { AppState } from "react-native-web";

274

275

// Get current state

276

console.log('Current state:', AppState.currentState);

277

278

// Listen for state changes

279

function AppStateExample() {

280

const [appState, setAppState] = useState(AppState.currentState);

281

282

useEffect(() => {

283

const handleAppStateChange = (nextAppState) => {

284

console.log('App state changed to:', nextAppState);

285

setAppState(nextAppState);

286

};

287

288

const subscription = AppState.addEventListener('change', handleAppStateChange);

289

290

return () => {

291

subscription?.remove();

292

};

293

}, []);

294

295

return (

296

<View>

297

<Text>App State: {appState}</Text>

298

{appState === 'active' && (

299

<Text>App is active and visible</Text>

300

)}

301

{appState === 'background' && (

302

<Text>App is in background</Text>

303

)}

304

</View>

305

);

306

}

307

308

// Pause video when app goes to background

309

function VideoPlayer() {

310

const [isPaused, setIsPaused] = useState(false);

311

312

useEffect(() => {

313

const handleAppStateChange = (nextAppState) => {

314

if (nextAppState === 'background') {

315

setIsPaused(true);

316

} else if (nextAppState === 'active') {

317

// Optionally resume when app becomes active

318

// setIsPaused(false);

319

}

320

};

321

322

AppState.addEventListener('change', handleAppStateChange);

323

}, []);

324

325

return (

326

<Video

327

source={{ uri: 'video.mp4' }}

328

paused={isPaused}

329

onPress={() => setIsPaused(!isPaused)}

330

/>

331

);

332

}

333

334

// Auto-save when app goes to background

335

function AutoSaveForm() {

336

const [formData, setFormData] = useState({});

337

338

useEffect(() => {

339

const handleAppStateChange = (nextAppState) => {

340

if (nextAppState === 'background') {

341

// Save form data

342

localStorage.setItem('formData', JSON.stringify(formData));

343

}

344

};

345

346

AppState.addEventListener('change', handleAppStateChange);

347

}, [formData]);

348

349

// ... rest of component

350

}

351

```

352

353

## PixelRatio

354

355

Utilities for working with device pixel density and converting between logical and physical pixels.

356

357

```javascript { .api }

358

const PixelRatio: {

359

get: () => number;

360

getFontScale: () => number;

361

getPixelSizeForLayoutSize: (layoutSize: number) => number;

362

roundToNearestPixel: (layoutSize: number) => number;

363

};

364

```

365

366

**Methods:**

367

- `get()` - Returns device pixel ratio (e.g., 2 for Retina displays)

368

- `getFontScale()` - Returns font scale multiplier (always 1 on web)

369

- `getPixelSizeForLayoutSize(dp)` - Convert density-independent pixels to actual pixels

370

- `roundToNearestPixel(dp)` - Round to nearest pixel boundary

371

372

**Usage:**

373

```javascript

374

import { PixelRatio } from "react-native-web";

375

376

// Get pixel ratio

377

const pixelRatio = PixelRatio.get();

378

console.log('Device pixel ratio:', pixelRatio); // 2 on Retina displays

379

380

// Convert logical pixels to physical pixels

381

const logicalSize = 100;

382

const physicalSize = PixelRatio.getPixelSizeForLayoutSize(logicalSize);

383

console.log(`${logicalSize}dp = ${physicalSize}px`);

384

385

// Round to pixel boundaries for crisp rendering

386

const crispSize = PixelRatio.roundToNearestPixel(100.7);

387

console.log('Crisp size:', crispSize); // 100.5 on 2x display

388

389

// High-DPI image selection

390

function HighDPIImage({ src, alt, width, height }) {

391

const pixelRatio = PixelRatio.get();

392

const imageSrc = pixelRatio > 1 ? `${src}@2x.png` : `${src}.png`;

393

394

return (

395

<img

396

src={imageSrc}

397

alt={alt}

398

width={width}

399

height={height}

400

style={{

401

width: PixelRatio.roundToNearestPixel(width),

402

height: PixelRatio.roundToNearestPixel(height)

403

}}

404

/>

405

);

406

}

407

408

// Pixel-perfect borders

409

const styles = StyleSheet.create({

410

separator: {

411

height: 1 / PixelRatio.get(), // Always 1 physical pixel

412

backgroundColor: '#ccc'

413

},

414

crispContainer: {

415

width: PixelRatio.roundToNearestPixel(200.3),

416

height: PixelRatio.roundToNearestPixel(150.7),

417

borderWidth: 1 / PixelRatio.get()

418

}

419

});

420

```

421

422

## Keyboard

423

424

Keyboard utilities with limited web functionality. Mainly provides compatibility with React Native code.

425

426

```javascript { .api }

427

const Keyboard: {

428

dismiss: () => void;

429

addListener: (eventType: string, callback: Function) => EventSubscription;

430

removeListener: (eventType: string, callback: Function) => void;

431

removeAllListeners: (eventType: string) => void;

432

isVisible: () => boolean;

433

};

434

```

435

436

**Usage:**

437

```javascript

438

import { Keyboard } from "react-native-web";

439

440

// Dismiss keyboard (blurs active input)

441

function DismissKeyboard() {

442

const handleSubmit = () => {

443

// Process form

444

Keyboard.dismiss();

445

};

446

447

return (

448

<View>

449

<TextInput placeholder="Type here..." />

450

<Button title="Submit" onPress={handleSubmit} />

451

</View>

452

);

453

}

454

455

// Note: Keyboard event listeners are no-ops on web

456

// Use standard DOM events or Visual Viewport API for keyboard detection

457

function WebKeyboardDetection() {

458

const [keyboardVisible, setKeyboardVisible] = useState(false);

459

460

useEffect(() => {

461

function handleResize() {

462

// Simple heuristic: significant height reduction might indicate keyboard

463

const currentHeight = window.innerHeight;

464

const isKeyboardVisible = currentHeight < window.screen.height * 0.8;

465

setKeyboardVisible(isKeyboardVisible);

466

}

467

468

// Better approach: Use Visual Viewport API if available

469

if (window.visualViewport) {

470

window.visualViewport.addEventListener('resize', handleResize);

471

return () => window.visualViewport.removeEventListener('resize', handleResize);

472

} else {

473

window.addEventListener('resize', handleResize);

474

return () => window.removeEventListener('resize', handleResize);

475

}

476

}, []);

477

478

return (

479

<View style={{ paddingBottom: keyboardVisible ? 20 : 0 }}>

480

<Text>Keyboard is {keyboardVisible ? 'visible' : 'hidden'}</Text>

481

</View>

482

);

483

}

484

```

485

486

## BackHandler

487

488

Android back button handler. On web, this is a no-op for React Native compatibility.

489

490

```javascript { .api }

491

const BackHandler: {

492

addEventListener: () => EventSubscription;

493

removeEventListener: () => void;

494

exitApp: () => void;

495

};

496

```

497

498

**Usage:**

499

```javascript

500

import { BackHandler, Platform } from "react-native-web";

501

502

// Cross-platform back handling

503

function NavigationHandler() {

504

useEffect(() => {

505

if (Platform.OS !== 'web') {

506

const backHandler = BackHandler.addEventListener('hardwareBackPress', () => {

507

// Handle back button on Android

508

return true; // Prevent default behavior

509

});

510

511

return () => backHandler.remove();

512

}

513

}, []);

514

515

// For web, use browser history or custom navigation

516

return <YourComponent />;

517

}

518

519

// Web-specific back button handling

520

function WebBackButton() {

521

const navigate = useNavigate(); // React Router or similar

522

523

const handleBack = () => {

524

if (Platform.OS === 'web') {

525

// Use browser history

526

window.history.back();

527

// Or programmatic navigation

528

// navigate(-1);

529

} else {

530

// Let BackHandler handle it on mobile

531

}

532

};

533

534

return (

535

<TouchableOpacity onPress={handleBack}>

536

<Text>← Back</Text>

537

</TouchableOpacity>

538

);

539

}

540

```

541

542

## DeviceEventEmitter

543

544

Global event emitter for cross-component communication and device events.

545

546

```javascript { .api }

547

const DeviceEventEmitter: {

548

addListener: (eventType: string, callback: Function) => EventSubscription;

549

removeListener: (eventType: string, callback: Function) => void;

550

emit: (eventType: string, data?: any) => void;

551

removeAllListeners: (eventType?: string) => void;

552

};

553

```

554

555

**Usage:**

556

```javascript

557

import { DeviceEventEmitter } from "react-native-web";

558

559

// Global event communication

560

function EventEmitterExample() {

561

useEffect(() => {

562

const subscription = DeviceEventEmitter.addListener('customEvent', (data) => {

563

console.log('Received event:', data);

564

});

565

566

return () => subscription.remove();

567

}, []);

568

569

const emitEvent = () => {

570

DeviceEventEmitter.emit('customEvent', {

571

message: 'Hello from emitter!',

572

timestamp: Date.now()

573

});

574

};

575

576

return (

577

<Button title="Emit Event" onPress={emitEvent} />

578

);

579

}

580

581

// Cross-component notifications

582

class NotificationService {

583

static notify(type, message) {

584

DeviceEventEmitter.emit('notification', { type, message });

585

}

586

587

static success(message) {

588

this.notify('success', message);

589

}

590

591

static error(message) {

592

this.notify('error', message);

593

}

594

}

595

596

function NotificationListener() {

597

const [notification, setNotification] = useState(null);

598

599

useEffect(() => {

600

const subscription = DeviceEventEmitter.addListener('notification', (data) => {

601

setNotification(data);

602

setTimeout(() => setNotification(null), 3000);

603

});

604

605

return () => subscription.remove();

606

}, []);

607

608

if (!notification) return null;

609

610

return (

611

<View style={[

612

styles.notification,

613

{ backgroundColor: notification.type === 'error' ? 'red' : 'green' }

614

]}>

615

<Text style={styles.notificationText}>{notification.message}</Text>

616

</View>

617

);

618

}

619

```

620

621

## Types

622

623

```javascript { .api }

624

interface DisplayMetrics {

625

width: number;

626

height: number;

627

scale: number;

628

fontScale: number;

629

}

630

631

interface DimensionsValue {

632

window: DisplayMetrics;

633

screen: DisplayMetrics;

634

}

635

636

interface EventSubscription {

637

remove: () => void;

638

}

639

640

interface PlatformStatic {

641

OS: 'web';

642

Version: string;

643

isTesting: boolean;

644

select: <T>(specifics: {web?: T, default?: T}) => T;

645

}

646

647

interface DimensionsStatic {

648

get: (dimension: 'window' | 'screen') => DisplayMetrics;

649

addEventListener: (type: 'change', handler: (dimensions: DimensionsValue) => void) => EventSubscription;

650

removeEventListener: (type: 'change', handler: Function) => void;

651

set: (initialDimensions?: DimensionsValue) => void;

652

}

653

654

interface AppStateStatic {

655

currentState: 'active' | 'background';

656

isAvailable: boolean;

657

addEventListener: (type: 'change' | 'memoryWarning', handler: (state: string) => void) => EventSubscription | undefined;

658

}

659

660

interface PixelRatioStatic {

661

get: () => number;

662

getFontScale: () => number;

663

getPixelSizeForLayoutSize: (layoutSize: number) => number;

664

roundToNearestPixel: (layoutSize: number) => number;

665

}

666

667

interface KeyboardStatic {

668

dismiss: () => void;

669

addListener: (eventType: string, callback: Function) => EventSubscription;

670

removeListener: (eventType: string, callback: Function) => void;

671

removeAllListeners: (eventType?: string) => void;

672

isVisible: () => boolean;

673

}

674

675

interface BackHandlerStatic {

676

addEventListener: (type: 'hardwareBackPress', handler: () => boolean) => EventSubscription;

677

removeEventListener: (type: 'hardwareBackPress', handler: Function) => void;

678

exitApp: () => void;

679

}

680

681

interface StatusBarProps {

682

animated?: boolean;

683

backgroundColor?: string;

684

barStyle?: 'default' | 'light-content' | 'dark-content';

685

hidden?: boolean;

686

networkActivityIndicatorVisible?: boolean;

687

showHideTransition?: 'fade' | 'slide';

688

translucent?: boolean;

689

}

690

```