The JavaScript Runtime and Hermes

This content was generated with the assistance of AI and should be verified against the official documentation before being relied on in production; see the section bibliography for the reference material consulted while preparing these pages.

Places's code is JavaScript/TypeScript, but it runs in neither a browser nor Node.js — it runs inside Hermes, React Native’s own JavaScript engine, after Metro and Babel have already transformed it. This page covers that environment: what syntax gets transformed, which globals actually exist, and why Hermes — rather than a browser’s JavaScript engine — is what executes the result.

What Metro and Babel Transform

Metro (covered in full on Tooling: Metro and Project Structure, later in this section) bundles Places's source, and @react-native/babel-preset is what actually transforms it on the way in — modern syntax the target Hermes version does not parse natively (JSX, newer ECMAScript proposals, TypeScript’s own type syntax) is compiled down to something Hermes accepts, the same role Babel plays for a web app targeting older browsers.

The Available Globals

A React Native app has no document and no window-as-DOM — there is no page to query and no DOM tree to walk — but it is not a bare JavaScript sandbox either: the platform explicitly polyfills a browser-shaped subset of globals so that ordinary networking and timing code, and much of the npm ecosystem written against those APIs, works unmodified.

Available Not available

fetch, Request, Response, Headers

document, any DOM node or DOM API

URL, URLSearchParams

window as a DOM/browsing-context object

FormData, Blob, File

localStorage, sessionStorage

AbortController, AbortSignal

XMLHttpRequest’s browser-specific quirks (a polyfilled `fetch-backed version exists, but there is no real browser networking stack underneath)

setTimeout, setInterval, setImmediate

CSS or any stylesheet-related global

requestAnimationFrame, requestIdleCallback

performance.now()

console.log/warn/error (routed to the native logs and, in a development build, React Native DevTools)

DEV (a boolean, true in a development build, false in a release build — React Native’s own equivalent of checking process.env.NODE_ENV)

Local persistence has no localStorage equivalent built in; Places uses expo-sqlite for its own data (introduced on Storage and Offline Support, later in this section) precisely because no browser-shaped storage global exists to fall back to.

Hermes: a Purpose-Built Engine

Hermes is a JavaScript engine Meta built specifically for React Native, rather than embedding an existing browser engine, for two reasons that matter directly to a mobile app’s user-perceived quality:

  • Startup time. Hermes compiles JavaScript to bytecode ahead of time, as part of the app’s build, rather than parsing and compiling source JavaScript on the device the moment the app launches — the app’s first screen appears sooner because the device only ever has to load and execute bytecode, never parse source.

  • Memory. Hermes’s bytecode format and runtime were designed for a memory-constrained mobile process, not a desktop browser tab, which keeps Places's baseline memory footprint lower than a general-purpose engine not tuned for that constraint would allow.

Hermes V1 has been the default engine since React Native 0.84, meaning Places — created fresh on 0.87 — uses it with no configuration. A project needing to pin the previous Hermes compiler generation temporarily (migrating a large codebase off a Hermes-V1-only behavior, for example) can still select it explicitly through the native build configuration (Android’s gradle.properties hermesFlags, iOS’s Podfile Hermes options), but Places takes no such override.

JavaScriptCore and V8: History

Before Hermes existed, React Native ran on JavaScriptCore (Apple’s engine, also usable as an explicit alternative to Hermes on some configurations) and, less commonly, V8 through some custom builds. Both remain mentionable when reading pre-Hermes-era blog posts or a very old codebase’s native build files, but neither is a supported alternative in a current 0.87 project — Hermes is the only engine Places, or any React Native app created today, ships with.

Timers and requestIdleCallback Replace InteractionManager

InteractionManager — previously used to defer expensive work until running animations/interactions finished — was removed in React Native 0.87. Its job is now covered directly by the standard timer and idle-callback globals already listed above: requestIdleCallback defers work to whenever the JavaScript thread is actually idle (the direct replacement for InteractionManager.runAfterInteractions), and an ordinary setTimeout remains the right tool wherever a fixed delay, rather than "whenever the thread is free," is what is actually needed.

Code: Hermes Capability, AbortController Fetch, and requestIdleCallback

A runtime check for Hermes — rarely needed in Places itself, since 0.87 always ships it, but useful in a library that must also run under a non-Hermes engine or under Node.js during testing:

declare const HermesInternal: object | undefined;

export function isRunningOnHermes(): boolean {
  return typeof HermesInternal !== 'undefined';
}

Places's apiClient.ts (introduced earlier as the fetch + AbortController wrapper) uses AbortController to cancel a request that takes too long, rather than leaving it to resolve into a screen the user has already navigated away from:

// src/lib/apiClient.ts (excerpt)
export async function fetchWithTimeout(url: string, timeoutMs = 8000): Promise<Response> {
  const controller = new AbortController();
  const timeoutId = setTimeout(() => controller.abort(), timeoutMs);

  try {
    return await fetch(url, { signal: controller.signal });
  } finally {
    clearTimeout(timeoutId);
  }
}

Deferring a non-urgent computation — re-sorting Places's cached list after a background sync, for example — until the JavaScript thread is actually idle:

function scheduleIdleResort(resort: () => void) {
  requestIdleCallback(
    (deadline) => {
      if (deadline.timeRemaining() > 0 || deadline.didTimeout) {
        resort();
      }
    },
    { timeout: 2000 },
  );
}

async/await and Promises themselves — the language feature both snippets above build on — are not React-Native-specific; see Asynchronous JavaScript for the full treatment of Promise states, chaining, and async/await.

Places’s TypeScript source transformed by Babel and bundled by Metro into a single JavaScript bundle