Using Expo Libraries on Horizon OS: A Guide to Compatibility

Authors
Jan Jaworski
Software Engineer
@
Callstack

Hello there!

This is the third blog post in a series covering React Native development for Meta Quest. The goal is to cover all important topics that will allow you to utilize your knowledge on a different platform. This way, you won’t run into unexpected hurdles and can stay productive to provide great experiences for your users.

We’ve already highlighted the starting flow for scaffolding a new app using Expo. As mentioned there, there are a few edge cases that we need to take into account when building React Native apps for Meta Quest, such as adjusted Expo project settings, testing on a Quest device, or an (upcoming) emulator, and library compatibility differences.

The good news is that most libraries you know and love will work out of the box. That said, there are a few important differences that you should consider avoiding problems when building your app. Let’s go over the details and look into some examples of what works, what needs additional setup, and what won’t work.

The React Native ecosystem is really big, so to keep things manageable, we’ll focus on libraries provided by Expo; the full list is available here.

Why compatibility isn't 1:1

Meta Quest runs on Meta Horizon OS, a customized version of Android built specifically for virtual and mixed reality experiences. It uses the same AOSP (Android Open Source Project) foundation but has been adapted for experiences in VR. Meta also added its own set of APIs and system services to support VR input, rendering, and performance management.

Because of these differences, there are a few areas where the standard Android assumptions don’t apply. Meta Horizon OS doesn’t include Google Play Services, so libraries depending on them, like Google Maps or Firebase (that depend on GMS), won’t work. The platform also uses a modified set of permissions, so certain Android permissions or features are restricted or replaced with VR-specific equivalents.

What works

Having said that, most things that don’t fall into the categories mentioned above should just work. This means that you can use core Expo libraries such as expo-file-system, expo-asset, or expo-sqlite out of the box. You can build gallery browsers, collaborative whiteboards, or media control panels without hitting compatibility issues. Even more complex experiences that rely heavily on state management, networking, or animations are fully supported.

In short, if a library is self-contained and doesn’t depend on external services or restricted hardware, it should work on Meta Horizon OS. This makes Expo a great choice for starting VR or mixed-reality projects quickly, while keeping the same workflow you’re used to from mobile development.

What needs work

Expo projects are configured for mobile Android by default. Meta Quest has a few project requirements that you need to change to build and release your app to the Meta Horizon Store. You could do it manually, but Software Mansion prepared an expo-horizon-core plugin that does all the necessary steps for you. The article provides more details and explains how to add it to your project.

expo-horizon-core will help you remove and skip forbidden properties, configure Android product flavors, specify Meta Horizon App ID, and provide Meta Quest-specific JS utilities. Make sure you have it installed and configured before you develop other features for your app.

Once your project is configured, you're ready to build most features. However, certain libraries that depend on Google Play Services or other unsupported Android APIs won't work out of the box. In such cases, you'll need Quest-compatible alternatives. Fortunately, several popular libraries already have solutions available. Let's look at three common examples: in-app purchases, notifications, and location services.

expo-iap

Unlike standard Android, which uses Google Play Billing, Meta Quest has its own billing infrastructure: the Meta Horizon Billing SDK. This platform difference means you need a library that supports the Meta-provided SDK.

The expo-iap library has recently been updated to support Meta Horizon OS, making the integration of payments in your app much easier. It provides a single, cross-platform API for handling in-app purchases. To get started, follow the expo-iap guide and pay close attention to the Meta Horizon OS-specific setup instructions.

Integration with payment solutions will allow you to offer users additional features. Depending on what type of application you’re building, here are some ideas on what you can use it for:

  • Unlocking premium content like advanced lessons, exclusive experiences, or additional app sections.
  • Selling customization options such as avatars, themes, or personalized environments.
  • Offering subscription access to regularly updated content, cloud features, or ad-free experiences.
  • Providing virtual items like in-game currency, creative assets, or tool upgrades.
  • Enabling one-time purchases for full app unlock, export features, or advanced capabilities.

expo-horizon-notifications

If you want to use notifications, you'll need to switch to expo-horizon-notifications, a Quest-compatible fork of expo-notifications. The APIs are identical, and both libraries are maintained in sync, so migration is straightforward. Just update your imports; everything else in your code stays the same.

Note that Expo Push Service is not supported on Meta Quest. For complete API documentation, refer to the expo-notifications docs.

Here’s a short example of how you can use notifications on Meta Quest:

// import notifications package
import * as Notifications from 'expo-horizon-notifications';

// setup notifications settings
Notifications.setNotificationHandler({
  handleNotification: async () => ({
    shouldPlaySound: false,
    shouldSetBadge: false,
    shouldShowBanner: true,
    shouldShowList: true,
  }),
});

// trigger notification by calling method somewhere in your application
Notifications.scheduleNotificationAsync({
  content: {
    title: 'Notification!',
    body: "It work on Meta Horizon OS!",
  },
  trigger: null,
});

Here’s how it should look when you run it on your Meta Quest:

expo-horizon-location

For location services, use expo-horizon-location as a drop-in replacement. Like the notifications library, it maintains the same API, so you only need to update your import.

However, there's an important difference to be aware of: Meta Quest devices don't have GPS hardware. Location is determined through WiFi networks and IP geolocation, which provides city or region-level accuracy at best. This makes it suitable for use cases like:

  • Determining user timezone for scheduling features.
  • Showing region-appropriate content or language defaults.
  • Analytics about user distribution by location.
  • Approximate location for social features, e.g., users in the same city.

Here’s a short example of how you can use location on Meta Quest:

// import location package
import * as Location from "expo-horizon-location";

// request location permission and get current position
async function getCurrentLocation() {
  let { status } = await Location.requestForegroundPermissionsAsync();
  if (status !== "granted") {
    setErrorMsg("Permission to access location was denied");
    return;
  }

  let location = await Location.getCurrentPositionAsync();
  return location;
}
Access location on Meta Quest
Access location on Meta Quest

There are other important considerations that are worth keeping in mind, but should not be blockers, such as design guidelines, which are different from what you might be used to on mobile. Hit targets, navigation, and typography also need refinement, e.g., if you want to use a popular component library, you might need to make adjustments to accommodate your app for VR.

If you want to write cross-platform code for Meta Quest and other devices, you might need to use the isHorizonDevice() method from expo-horizon-core to guard against executing platform-specific logic on platforms that do not support it.

Other libraries, like expo-sms or expo-sensors, provide feature-specific checks that should be used to avoid executing unsupported logic.

What doesn’t work

Let’s review a few examples of things not supported on Meta Quest. It’s by no means comprehensive, but it should give you a good idea of what to look out for and how to handle such cases.

When developing for Meta Horizon OS, you need to pay attention to:

  • Mobile-specific hardware & APIs: Cellular (SMS & Calling), GPS
  • Libraries and dependencies using Google Play Services (GPS): Google Authentication, Notifications, Billing
  • Prohibited and unsupported permissions from Android: Dangerous (e.g., INSTALL_PACKAGES), or irrelevant for VR devices (e.g., CALL_PHONE)

They are not supported, and you’ll have to find alternatives.

If you set up your project using expo-horizon-core, things like prohibited permissions will be disabled.

Summary

You’re one step closer to utilizing the full potential of React Native and Expo without running into unexpected issues. Having read this article, you’ll know how to navigate the Meta Quest ecosystem and what to do to reach out for an alternative solution.

In the near future, we will cover how to prepare your app for release to the Meta Horizon Store, so stay tuned!

Insights

Learn more about Virtual Reality

cover

Oct 15Article

Multi-Window React Native Apps With the Meta VR Layout SDK

Build multi-window React Native apps for Meta Quest with the Meta VR Layout SDK. Learn shared state, fallbacks, positioning, and simulator testing.

cover

Dec 8Article

Mobile vs. VR: Key Differences in Features, UI, and UX

Explore how VR reshapes layout behavior, typography, spacing, accessibility expectations, and interaction patterns in React Native.

cover

Dec 4Live Stream

React Native on Meta Quest: What We Learned About Building for VR

See Meta Quest used as a practical dev setup and watch the team build and test React Native features directly inside a VR workspace.

cover

Nov 26Podcast

Bringing React Native to VR on Meta Quest

Learn how Meta Quest uses React Native for 2D panels in 3D spaces, why AOSP matters, and how developers can bring existing RN apps to VR with minimal friction.

cover

Nov 20Article

How to Release a React Native App on the Meta Horizon Store

Learn how to build, submit, and publish your React Native app on the Meta Horizon Store.

cover

Nov 6Article

What You Can Build With React Native on Meta Quest

Get React Native app ideas for Meta Quest, inspired by Meta's $1.5M developer competition. Explore VR/MR concepts for meditation, games, and social apps.

cover

Oct 22Article

Getting Started With Expo on Meta Quest

Start building React Native apps for Meta Quest with Expo. This guide details the setup using Expo Go for fast iteration and explains how to use development builds for full native capabilities on Met Horizon OS.

cover

Oct 8Article

Learn Once, Write for VR: A React Native Showcase for Meta's HorizonOS

Learn how a React Native app was built for Meta's HorizonOS, proving its viability for creating performant, cross-platform mixed reality experiences.

cover

Mar 19Tutorial

The Hard Way vs. The React Native AI SDK Way: Stop Writing Custom Modules for Every Model

Learn how React Native AI uses a unified provider model to switch between cloud and on-device LLMs without rewriting application logic, enabling reliable AI experiences online and offline.

cover

Mar 12Tutorial

Build Smarter Apps: Tool Calling & AI Orchestration Explained

Learn how on-device models in React Native can call tools, interact with external APIs, and return structured outputs you can use directly in application logic.

cover

Mar 5Tutorial

MLC LLM + React Native: On-Device AI Without the Pain

Learn how to run third-party on-device LLMs in React Native using MLC LLM. Choose your models, run them efficiently across platforms, and keep a unified JavaScript API with React Native AI.

cover

Feb 24Tutorial

Intelligent Fallbacks with Apple Intelligence in React Native

Learn how to use Apple’s built-in Foundation Models in React Native apps, reduce memory usage, and enable fast, fully on-device AI with React Native AI Apple.

cover

Feb 10Tutorial

What Is the React Native AI SDK? A Complete Intro & Quickstart

Learn why on-device AI matters for React Native apps, how local LLMs behave offline, and what problems React Native AI is designed to solve from day one.

cover

Jan 29Tutorial

Implementing an Android TurboModule with Kotlin

Learn how to implement a React Native TurboModule on Android, from the TypeScript spec and Codegen to the Kotlin implementation and package registration.

cover

Jan 19Tutorial

Working with Different Threads in Swift TurboModules

Learn how JavaScript, native module, background, and main threads interact in React Native TurboModules, and how to use GCD queues to run async work safely in Swift.

cover

Jan 9Tutorial

How to Add Type-Safe Constants to Swift TurboModules

Learn how to expose typed, immutable constants from a Swift TurboModule using Codegen, and get full end-to-end type safety between native and JavaScript.

cover

Dec 30Tutorial

Adding Event Emitters to Your TurboModule in Swift

Learn how to emit native events from a Swift TurboModule and subscribe to them in JavaScript, including proper memory management and Codegen integration.

cover

Dec 22Tutorial

Writing Your First TurboModule in Swift

Learn how to build a fully working TurboModule in Swift, integrate it with Codegen, and bridge it through Objective‑C inside an Expo app.