pmndrs/xr: Adding WebXR to a react-three-fiber App
🤳 VR/AR for react-three-fiber
At a glance
- What is it?
- The pmndrs/xr package turns an existing react-three-fiber scene into a WebXR experience through a store, an enterAR or enterVR call, and an XR wrapper. It is for React developers already comfortable with three.js, and it inherits every limitation of the browser's WebXR support.
- Who is it for?
- Adopt pmndrs/xr if you already ship a react-three-fiber scene and want it to run in a headset or on a phone without rewriting your component tree. Do not adopt it if you have no WebXR-capable target device, or if you need tracked body input, which the README lists only as roadmap.
- Can I use it commercially?
- Check first. The repository uses a licence we do not classify automatically, so read its LICENSE file before any commercial use.
- Is it still maintained?
- Yes. The repository last received commits 2 days ago.
- What is it written in?
- Mainly TypeScript, according to GitHub's language statistics.
Answers come from the project's GitHub data, last synced on September 30, 2026, and from our analysis. They are not legal advice.
Editorial analysis
The gap pmndrs/xr fills between a three.js scene and a headset
A react-three-fiber scene renders to a canvas on a flat page. Making that same scene appear in a headset or anchored to a phone camera means talking to the WebXR device API directly: requesting a session, binding a reference space, wiring input sources, and mapping controller or hand poses into the scene graph. pmndrs/xr exists to absorb that work. The README describes its purpose in one line: "Turn any R3F app into an interactive immersive experience." The audience is narrow and specific. You need an existing react-three-fiber app, or the willingness to write one, plus familiarity with React state and hooks. If your scene is written in plain three.js without React, the store and XR component model will not map onto your code. If you are building a native Quest or Vision Pro application, this is the wrong layer entirely, because everything here runs in a browser tab.
How the store, the XR component and pointer events fit together
The architecture has three moving parts. A store created with createXRStore() holds session state and exposes the entry points, enterAR() and enterVR(). The XR component wraps the contents of a Canvas and connects that store to the renderer, so meshes inside it receive XR-aware input. From there, ordinary react-three-fiber pointer events keep working: the README's basic example toggles a mesh material between red and blue on click, and notes the click arrives "through touching or pointing", meaning the same handler fires whether the user taps a phone screen or aims a controller at the object. The store is also the place where options live, and the tutorials list separate pages for interactions, handles, origin, teleport, gamepad, secondary input sources, layers, anchors, DOM overlays, hit test and guards. Hit test and anchors are the AR-side pieces: hit test places content against detected surfaces, anchors keep it pinned there. Guards appear to gate whether an interaction is allowed to proceed. The README does not describe the internal session lifecycle, so how the store reconciles a session ending on the device with React state is not documented in the pages the project links to.
Installing @react-three/xr and getting a clickable mesh into AR
The README gives one install command covering the peer packages. three and @react-three/fiber are installed alongside the XR package, and the @latest tag is used for the XR package itself.
npm install three @react-three/fiber @react-three/xr@latestAfter that, the README's own example is the shortest path to something running. A store is created at module scope, a button outside the Canvas calls enterAR(), and the mesh sits inside the XR wrapper.
import { Canvas } from '@react-three/fiber'
import { XR, createXRStore } from '@react-three/xr'
import { useState } from 'react'
const store = createXRStore()The component then renders the button and the Canvas, with the store passed to XR as a prop and the mesh using pointerEventsType to deny the grab interaction.
<button onClick={() => store.enterAR()}>Enter AR</button>
<Canvas>
<XR store={store}>
<mesh pointerEventsType={{ deny: 'grab' }} onClick={() => setRed(!red)} position={[0, 1, -1]}>
<boxGeometry />
<meshBasicMaterial color={red ? 'red' : 'blue'} />
</mesh>
</XR>
</Canvas>What you should see, per the README, is a mesh that changes colour when clicked by touching or pointing. The README also points to a longer guide for converting an existing react-three-fiber app, and the repository ships an examples/ directory with runnable projects including handheld-ar, hit-testing, pointer-events and vanilla. Those are the practical starting points; the README does not give a command for running them.
Where pmndrs/xr stops: device support, body tracking and the licence file
The hard boundary is WebXR itself. The package requests a session from the browser, so if the browser or device does not expose the session mode you call, enterAR() or enterVR() has nothing to attach to. The README does not document a fallback path or an error surface for that case. Second, the roadmap lists XR Gestures and Tracked Body as future work, which means tracked body input is not a capability you should plan around today. Third, the version history is not fast-moving: the most recent release listed is v6.6.0 from 2025-01-30, with v6.4.0 and v6.3.0 both from 2024-11-01. The last push to the repository was on 2026-05-29, so there is ongoing work between releases, but the published version numbers have not moved in a long time. Finally, the licence is ambiguous in the metadata: the repository's package.json says MIT, while the repository's own licence field is reported as NOASSERTION. That discrepancy is worth resolving by reading the LICENSE file before you depend on it commercially.
pmndrs/xr against writing WebXR by hand, and against A-Frame
The closest alternative in spirit is writing the WebXR device API calls yourself against a three.js renderer. That gives you full control over session options, reference spaces and input source handling, and it removes a dependency. The cost is that you reimplement the mapping from XR input sources to scene objects, plus the React integration that keeps session state in sync with your component tree. pmndrs/xr's value is that mapping, and the README's example shows how little of it leaks into application code. A different alternative is A-Frame, which takes an entity-component approach with HTML-style markup rather than React components. If your team writes JSX and already uses react-three-fiber, pmndrs/xr fits the existing mental model; A-Frame would mean adopting a second, unrelated way of describing a scene. If your team does not use React at all, A-Frame is the more natural choice, and pmndrs/xr is not.
Who should adopt pmndrs/xr, and what to verify before you do
Adopt it if you have a react-three-fiber app and a concrete WebXR target: a Quest browser, an Android phone with ARCore, or a desktop headset browser. The three-step pattern in the README, create the store, call enterAR() or enterVR() from a button, wrap content in XR, is small enough to try on an existing scene in an afternoon. Skip it if you are targeting native platforms, if you need tracked body input, or if your scene is not built on React. Before you build on it, check three things. Read the LICENSE file to settle the MIT versus NOASSERTION discrepancy. Confirm your target browser supports the session mode you plan to call, since the README documents no fallback. And check the release history against your dependency policy: a newest release of v6.6.0 dated 2025-01-30, with the repository last pushed on 2026-05-29, means unreleased changes may be ahead of the published package.
Editorial conclusion
Adopt pmndrs/xr if you already ship a react-three-fiber scene and want it to run in a headset or on a phone without rewriting your component tree. Do not adopt it if you have no WebXR-capable target device, or if you need tracked body input, which the README lists only as roadmap. Before committing, verify the licence text in the repository's LICENSE file, since the package metadata says MIT while the repository metadata says NOASSERTION, and confirm that your target browser exposes the WebXR session modes you intend to call.
Frequently asked questions
What does pmndrs/xr install alongside itself?
The README's install command is npm install three @react-three/fiber @react-three/xr@latest, so three and @react-three/fiber are installed together with the XR package.
Does pmndrs/xr support tracked body input?
No. The README lists Tracked Body under the roadmap, alongside XR Gestures, so it is planned rather than available.
Which licence does pmndrs/xr use?
The repository's package.json states MIT, while the repository's licence field is reported as NOASSERTION. The LICENSE file in the repository is the place to check which applies.
What does XR stand for?
The README does not expand the acronym. It uses XR as the umbrella term for the immersive experiences the package enables, alongside the separate AR and VR entry points enterAR() and enterVR().
Official sources
Add this badge to your README
If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.
[](https://hysenlabs.com/projects/pmndrs-xr)