> ## Documentation Index
> Fetch the complete documentation index at: https://docs.go.gbgplc.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Hello Journey

> A minimal React app that loads a GBG GO journey.

A minimal integration that loads a web-based identity journey inside a React app. This example is the smallest possible working bridge integration — a `BridgeHostProvider`, an `<iframe>`, and one capability handler.

## What this example demonstrates

* Instantiating `BridgeHostProvider` with origin allowlist and capabilities
* Declaring document-capture support via the `capabilities` prop
* Registering a handler with `useBridgeCapability`
* Surfacing internal bridge errors via `useBridgeHostError`

## Complete source

```tsx theme={null}
import {
  BridgeHostProvider,
  useBridgeCapability,
  useBridgeHostError,
} from "@gbg/go-bridge-web-react";
import { useEffect, useState } from "react";

const JOURNEY_URL = "https://journey.example.com";

export default function App() {
  const [iframe, setIframe] = useState<HTMLIFrameElement | null>(null);
  const [permissionState, setPermissionState] = useState("prompt");

  useEffect(() => {
    if (!navigator.permissions) return;
    navigator.permissions
      .query({ name: "camera" as PermissionName })
      .then((status) => setPermissionState(status.state))
      .catch(() => undefined);
  }, []);

  return (
    <BridgeHostProvider
      iframe={iframe}
      allowedOrigins={[new URL(JOURNEY_URL).origin]}
      hostVersion="1.0.0"
      capabilities={{
        "camera.document": {
          supported: true,
          version: "1.0",
          permissionState,
        },
      }}
    >
      <ErrorBanner />
      <DocumentCaptureHandler />
      <iframe
        ref={setIframe}
        src={JOURNEY_URL}
        allow="camera"
        style={{ width: "100vw", height: "100vh", border: 0 }}
      />
    </BridgeHostProvider>
  );
}

function DocumentCaptureHandler() {
  useBridgeCapability("camera.document.capture", {
    handle: async (_request, responder) => {
      // Your capture UI here — return a base64 image
      const image = "";
      responder.success({ image });
    },
  });
  return null;
}

function ErrorBanner() {
  const [error, setError] = useState<Error | null>(null);
  useBridgeHostError(setError);
  if (!error) return null;
  return (
    <div
      role="alert"
      style={{
        position: "fixed",
        top: 12,
        left: 12,
        right: 12,
        background: "crimson",
        color: "white",
        padding: 12,
        borderRadius: 8,
        zIndex: 1000,
      }}
    >
      {error.message}
      <button
        onClick={() => setError(null)}
        style={{
          float: "right",
          background: "transparent",
          border: 0,
          color: "white",
          cursor: "pointer",
        }}
      >
        ×
      </button>
    </div>
  );
}
```

## How it works

1. **`BridgeHostProvider`** wraps the iframe-rendering subtree. The provider waits for `iframe` to transition from `null` to a real element, then instantiates the underlying `BridgeHost` and snapshots `allowedOrigins`, `hostVersion`, and `capabilities`.

2. **`useState` for the iframe element** is required — not `useRef`. The provider's effect depends on `iframe`, and refs don't trigger re-renders when the iframe element mounts.

3. **`navigator.permissions.query`** reads the current camera permission state. Passing it through `CapabilityInfo.permissionState` lets the journey decide whether to surface a permission prompt before triggering capture.

4. **`useBridgeCapability`** registers a handler for the `"camera.document.capture"` action. On mount it calls `host.registerHandler`; on unmount it deregisters (only if its own proxy is still the active handler).

5. **`useBridgeHostError`** subscribes to internal SDK errors (handler exceptions, send failures, delegate throws). Lifted into local state so the banner re-renders when an error occurs.

## What happens at runtime

```
1. App mounts → BridgeHostProvider renders with iframe = null
2. iframe element mounts → setIframe(element) → provider's effect fires
3. Provider builds BridgeHost(iframe, allowedOrigins, ...), sets up window message listener
4. Built-in CapabilityQueryHandler auto-registers for "capability.query"
5. Journey loads inside iframe
6. Journey sends: capability.query request
7. Host responds: { environment: "web", hostVersion: "1.0.0", capabilities: { camera.document: { supported: true, ... } } }
8. Journey adapts UI based on the response and your permission state
9. Journey sends: camera.document.capture request when user reaches the capture step
10. Handler runs, returns an image, responder.success({ image }) sent back
```

## Required page configuration

For this minimal example, no special configuration beyond a normal React app is needed. In production, ensure your host page sets:

* **`Content-Security-Policy: frame-src https://journey.example.com;`** — Allow the journey origin in your CSP.
* **`<iframe allow="camera">`** — Delegate camera permission to the iframe context.
* **HTTPS in production** — Mixed content (HTTPS host + HTTP iframe) is silently blocked by browsers.

## Next steps

* [Two-Way Communication](/docs/go-v2/developer-integration/sdks/web/examples/two-way-communication) — Send events to the journey and observe inbound traffic.
* [Advanced Integration](/docs/go-v2/developer-integration/sdks/web/examples/advanced-integration) — Production patterns with pre-launch validation, lifecycle, and error states.
* [Tutorial](/docs/go-v2/developer-integration/sdks/web/tutorial) — Build the full app with backend token exchange.
