expo-horizon

Compare original and translation side by side

🇺🇸

Original

English
🇨🇳

Translation

Chinese

Expo Horizon: Migrating Expo SDK to Meta Quest

Expo Horizon:将 Expo SDK 迁移至 Meta Quest

Software Mansion's production guide for adding Meta Quest support to Expo apps using the expo-horizon packages.
This skill does not bundle a copy of the docs. For any task below, always webfetch the linked official README or Meta documentation page to get up-to-date installation steps, plugin options, API surface, and feature matrices. This skill only captures the decision tree, critical rules, and non-obvious gotchas that agents routinely miss.
Software Mansion 出品的生产级指南,介绍如何使用 expo-horizon 包为 Expo 应用添加 Meta Quest 支持。
本技能未包含文档副本。 对于以下任何任务,请务必联网获取链接中的官方 README 或 Meta 文档页面,以获取最新的安装步骤、插件选项、API 接口和功能矩阵。本技能仅涵盖决策流程、关键规则以及智能体常忽略的隐性陷阱。

Decision Tree

决策流程

What do you need to do?
├── Starting from scratch or adding Quest support to an existing Expo app?
│   └── Follow the "Setup Workflow" below (do NOT auto-install location or
│       notifications packages) and webfetch: expo-horizon-core README
│       ├── Install expo-horizon-core
│       ├── Configure the config plugin (horizonAppId, panel size, supportedDevices)
│       ├── Add quest/mobile build scripts
│       ├── Add runtime device detection (isHorizonDevice, isHorizonBuild)
│       └── Detect expo-location / expo-notifications, then ASK before migrating
├── Need location services on Quest?
│   └── Webfetch: expo-horizon-location README
│       ├── Replace expo-location with expo-horizon-location
│       ├── Review the feature support matrix
│       └── Guard unsupported calls (heading, geocoding, geofencing, background)
├── Need push notifications on Quest?
│   └── Webfetch: expo-horizon-notifications README
│       ├── Replace expo-notifications with expo-horizon-notifications
│       ├── Configure horizonAppId in expo-horizon-core
│       ├── Use getDevicePushTokenAsync (Expo Push Service is not supported)
│       └── Skip badge counts (not supported on Quest)
└── Need to build, run, or publish for Quest?
    └── Webfetch: expo-horizon-core README (build variants) + Meta docs below
        ├── Build variants: questDebug, questRelease, mobileDebug, mobileRelease
        ├── Meta Quest Developer Hub (device management, sideloading)
        └── Meta Horizon Store manifest requirements
你需要完成什么操作?
├── 从零开始开发,还是为现有 Expo 应用添加 Quest 支持?
│   └── 遵循下方的「设置流程」(请勿自动安装定位或通知包)并联网获取:expo-horizon-core README
│       ├── 安装 expo-horizon-core
│       ├── 配置配置插件(horizonAppId、面板尺寸、supportedDevices)
│       ├── 添加 quest/mobile 构建脚本
│       ├── 添加运行时设备检测(isHorizonDevice、isHorizonBuild)
│       └── 检测 expo-location / expo-notifications,然后在迁移前询问用户
├── 需要在 Quest 上使用定位服务?
│   └── 联网获取:expo-horizon-location README
│       ├── 用 expo-horizon-location 替换 expo-location
│       ├── 查看功能支持矩阵
│       └── 对不支持的调用进行防护(方向、地理编码、地理围栏、后台定位)
├── 需要在 Quest 上使用推送通知?
│   └── 联网获取:expo-horizon-notifications README
│       ├── 用 expo-horizon-notifications 替换 expo-notifications
│       ├── 在 expo-horizon-core 中配置 horizonAppId
│       ├── 使用 getDevicePushTokenAsync(不支持 Expo 推送服务)
│       └── 跳过角标计数(Quest 不支持该功能)
└── 需要为 Quest 构建、运行或发布应用?
    └── 联网获取:expo-horizon-core README(构建变体)+ 下方的 Meta 文档
        ├── 构建变体:questDebug、questRelease、mobileDebug、mobileRelease
        ├── Meta Quest 开发者中心(设备管理、侧载)
        └── Meta Horizon Store 清单要求

Setup Workflow (adding Quest support to an existing Expo app)

设置流程(为现有 Expo 应用添加 Quest 支持)

Follow these steps in order when the user asks to add Meta Quest support. Do not combine steps 2 and 3 into a single install command — the sibling packages require explicit user confirmation.
  1. Install and configure
    expo-horizon-core
    .
    • Run
      npx expo install expo-horizon-core
      .
    • Ask the user for the config plugin values before writing them. Present all options in a single prompt so the user can paste custom values or accept defaults in one pass. Show each option on its own line with its default in brackets, e.g.:
      I'll add the
      expo-horizon-core
      config plugin to
      app.json
      . Please confirm or override each value (press Enter / reply "default" to accept the bracketed default):
      • supportedDevices
        [
        quest2|quest3|quest3s
        ] — pipe-separated Quest devices your app supports (required for Meta Horizon Store submission).
      • horizonAppId
        [empty] — Meta Horizon application ID. Leave empty unless you plan to use push notifications; required by
        expo-horizon-notifications
        to issue device push tokens.
      • defaultWidth
        [
        1024dp
        ] — Default panel width. Leave blank to omit.
      • defaultHeight
        [
        640dp
        ] — Default panel height. Leave blank to omit. If you set width/height, make sure your Expo
        orientation
        matches (use
        "landscape"
        for wide panels).
      • disableVrHeadtracking
        [
        false
        ] — Set
        true
        to omit the
        android.hardware.vr.headtracking
        manifest entry.
      • allowBackup
        [
        false
        ] — Meta recommends
        false
        for sensitive data; set
        true
        only if you need Android backup in the Quest build.
    • Only write the plugin config after the user replies. Omit any option the user left blank so the package's own default applies (don't write empty strings for
      horizonAppId
      ,
      defaultWidth
      , or
      defaultHeight
      ).
    • Add
      quest
      /
      mobile
      build scripts to
      package.json
      .
    • Run
      npx expo prebuild --clean
      .
    • Webfetch the expo-horizon-core README for current option names and defaults before asking — the defaults above can change between releases.
  2. Detect existing location / notification packages. Do NOT install the horizon equivalents yet.
    • Read the project's
      package.json
      .
    • Check
      dependencies
      and
      devDependencies
      for
      expo-location
      and
      expo-notifications
      .
    • If neither is present, skip the rest of this workflow — the user has no migration to do.
  3. For each detected package, ask the user before migrating.
    • If
      expo-location
      is found, ask:
      "I found
      expo-location
      in your project. Do you want me to replace it with
      expo-horizon-location
      so location works on Meta Quest? (Quest has no GPS, heading, geocoding, or geofencing — unsupported calls will need to be guarded with
      ExpoHorizon.isHorizonDevice
      .)"
    • If
      expo-notifications
      is found, ask:
      "I found
      expo-notifications
      in your project. Do you want me to replace it with
      expo-horizon-notifications
      so push notifications work on Meta Quest? This requires a
      horizonAppId
      in the core config plugin, uses Meta's push service (not the Expo Push Service), and does not support badge counts or
      getExpoPushTokenAsync
      ."
    • Present the questions together if both packages are present.
    • Wait for an explicit answer before running any install or edit for these packages.
  4. Only after the user confirms, perform the migration for the approved package(s):
    • Install the horizon equivalent (
      npx expo install expo-horizon-location
      or
      expo-horizon-notifications
      ).
    • Uninstall the original (
      npm uninstall expo-location
      or
      expo-notifications
      ).
    • Update all
      import
      statements to the horizon package name.
    • For notifications: ensure
      horizonAppId
      is set in the
      expo-horizon-core
      plugin config and add
      expo-horizon-notifications
      to the plugins array.
    • Run
      npx expo prebuild --clean
      again.
    • Webfetch the relevant README (location or notifications) for the full feature support matrix and guard unsupported calls behind
      ExpoHorizon.isHorizonDevice
      .
  5. If the user declines a migration, leave the original package untouched and note in the summary that feature X (location / notifications) will not work on the
    quest
    build until migrated.
当用户要求添加 Meta Quest 支持时,请按顺序执行以下步骤。请勿将步骤 2 和 3 合并为一个安装命令——相关包需要用户明确确认。
  1. 安装并配置
    expo-horizon-core
    • 运行
      npx expo install expo-horizon-core
    • 在写入配置前,请向用户确认配置插件的值。 将所有选项一次性展示给用户,以便用户可一次性粘贴自定义值或接受默认值。每个选项单独一行,并在括号中显示默认值,例如:
      我将把
      expo-horizon-core
      配置插件添加到
      app.json
      中。请确认或修改以下每个值(按回车/回复「default」接受括号中的默认值):
      • supportedDevices
        [
        quest2|quest3|quest3s
        ] — 应用支持的 Quest 设备,用竖线分隔(Meta Horizon Store 提交必填)。
      • horizonAppId
        [空] — Meta Horizon 应用 ID。除非计划使用推送通知,否则留空;
        expo-horizon-notifications
        需要此 ID 来生成设备推送令牌。
      • defaultWidth
        [
        1024dp
        ] — 默认面板宽度。留空则不设置。
      • defaultHeight
        [
        640dp
        ] — 默认面板高度。留空则不设置。如果设置了宽/高,请确保 Expo 的
        orientation
        与之匹配(宽面板使用
        "landscape"
        )。
      • disableVrHeadtracking
        [
        false
        ] — 设置为
        true
        可省略
        android.hardware.vr.headtracking
        清单条目。
      • allowBackup
        [
        false
        ] — Meta 建议对敏感数据设置为
        false
        ;仅当 Quest 构建需要 Android 备份时才设置为
        true
    • 仅在用户回复后写入插件配置。用户留空的选项请省略,以便使用包自身的默认值(请勿为
      horizonAppId
      defaultWidth
      defaultHeight
      写入空字符串)。
    • package.json
      中添加
      quest
      /
      mobile
      构建脚本。
    • 运行
      npx expo prebuild --clean
    • 在询问用户前,请联网获取 expo-horizon-core README 以获取当前的选项名称和默认值——上述默认值可能会随版本更新而变化。
  2. 检测现有的定位/通知包。请勿立即安装对应的 horizon 包。
    • 读取项目的
      package.json
    • 检查
      dependencies
      devDependencies
      中是否存在
      expo-location
      expo-notifications
    • 如果两者都不存在,则跳过此流程的剩余部分——用户无需进行迁移。
  3. 对于每个检测到的包,在迁移前询问用户。
    • 如果检测到
      expo-location
      ,询问:
      "我在你的项目中发现了
      expo-location
      。是否需要将其替换为
      expo-horizon-location
      以在 Meta Quest 上使用定位功能?(Quest 没有 GPS、方向传感器、地理编码或地理围栏——不支持的调用需要用
      ExpoHorizon.isHorizonDevice
      进行防护。)"
    • 如果检测到
      expo-notifications
      ,询问:
      "我在你的项目中发现了
      expo-notifications
      。是否需要将其替换为
      expo-horizon-notifications
      以在 Meta Quest 上使用推送通知?这需要在核心配置插件中设置
      horizonAppId
      ,使用 Meta 的推送服务(而非 Expo 推送服务),且不支持角标计数或
      getExpoPushTokenAsync
      。"
    • 如果同时检测到两个包,请将问题一并展示。
    • 在执行这些包的安装或编辑操作前,请等待用户的明确答复。
  4. 仅在用户确认后,对已批准的包执行迁移:
    • 安装对应的 horizon 包(
      npx expo install expo-horizon-location
      expo-horizon-notifications
      )。
    • 卸载原包(
      npm uninstall expo-location
      expo-notifications
      )。
    • 将所有
      import
      语句更新为新的包名。
    • 对于通知功能:确保
      expo-horizon-core
      插件配置中已设置
      horizonAppId
      ,并将
      expo-horizon-notifications
      添加到插件数组中。
    • 再次运行
      npx expo prebuild --clean
    • 联网获取对应的 README(定位或通知)以查看完整的功能支持矩阵,并使用
      ExpoHorizon.isHorizonDevice
      对不支持的调用进行防护。
  5. 如果用户拒绝迁移,请保留原包不变,并在总结中注明:在
    quest
    构建中,功能 X(定位/通知)将无法正常使用,直到完成迁移。

Critical Rules

关键规则

  • Always install
    expo-horizon-core
    first.
    It is required by all other expo-horizon packages and sets up the
    quest
    /
    mobile
    build flavors that other packages depend on.
  • Never auto-install
    expo-horizon-location
    or
    expo-horizon-notifications
    .
    When adding Quest support, detect existing
    expo-location
    /
    expo-notifications
    dependencies in
    package.json
    and ask the user whether to migrate each one. Install and configure only the packages the user explicitly approves. See the Setup Workflow above.
  • Use
    quest
    build variants only on Meta Quest devices.
    Running
    questDebug
    or
    questRelease
    builds on standard Android phones is unsupported and will behave unexpectedly.
  • Set
    supportedDevices
    in the config plugin.
    This is required for Meta Horizon Store submission. Use pipe-separated values:
    "quest2|quest3|quest3s"
    .
  • Run
    npx expo prebuild --clean
    after any plugin config change.
    The config plugin modifies native project files at prebuild time. Stale native projects will not reflect your changes.
  • Replace imports, not just packages. When migrating from
    expo-location
    or
    expo-notifications
    , update all import statements to use the new package names (
    expo-horizon-location
    ,
    expo-horizon-notifications
    ).
  • Quest has no GPS, magnetic sensors, or Geocoder. Features like heading, geocoding, reverse geocoding, and geofencing are unavailable on Quest. Guard these calls with
    ExpoHorizon.isHorizonDevice
    or
    ExpoHorizon.isHorizonBuild
    .
  • Push notifications require
    horizonAppId
    .
    Without it,
    getDevicePushTokenAsync
    will not return a valid token on Quest devices. Use
    getDevicePushTokenAsync
    (not
    getExpoPushTokenAsync
    ) on Quest; send the returned
    { type: 'horizon', data }
    token to your backend and deliver via Meta's push service.
  • isHorizonDevice
    vs
    isHorizonBuild
    .
    Use
    isHorizonDevice
    for runtime hardware checks (physical Quest detection). Use
    isHorizonBuild
    for build-time feature gating (which native code was compiled in).
  • Expo Go is not supported. You must use custom development builds via
    npx expo prebuild
    .
  • 请先安装
    expo-horizon-core
    它是所有其他 expo-horizon 包的依赖,负责设置其他包所需的
    quest
    /
    mobile
    构建变体。
  • 请勿自动安装
    expo-horizon-location
    expo-horizon-notifications
    添加 Quest 支持时,请检测
    package.json
    中现有的
    expo-location
    /
    expo-notifications
    依赖,并询问用户是否需要迁移每个包。仅安装和配置用户明确批准的包。请参阅上方的设置流程。
  • 仅在 Meta Quest 设备上使用
    quest
    构建变体。
    在标准安卓手机上运行
    questDebug
    questRelease
    构建不受支持,且会出现异常行为。
  • 在配置插件中设置
    supportedDevices
    这是 Meta Horizon Store 提交的必填项。使用竖线分隔的值:
    "quest2|quest3|quest3s"
  • 任何插件配置更改后,请运行
    npx expo prebuild --clean
    配置插件会在预构建阶段修改原生项目文件。过时的原生项目将无法反映你的更改。
  • 替换导入语句,而不仅仅是包。
    expo-location
    expo-notifications
    迁移时,请将所有导入语句更新为新的包名(
    expo-horizon-location
    expo-horizon-notifications
    )。
  • Quest 没有 GPS、磁力传感器或地理编码器。 方向、地理编码、逆地理编码和地理围栏等功能在 Quest 上不可用。请使用
    ExpoHorizon.isHorizonDevice
    ExpoHorizon.isHorizonBuild
    对这些调用进行防护。
  • 推送通知需要
    horizonAppId
    没有此 ID,
    getDevicePushTokenAsync
    在 Quest 设备上无法返回有效的令牌。在 Quest 上使用
    getDevicePushTokenAsync
    (而非
    getExpoPushTokenAsync
    );将返回的
    { type: 'horizon', data }
    令牌发送到你的后端,并通过 Meta 的推送服务进行推送。
  • isHorizonDevice
    vs
    isHorizonBuild
    使用
    isHorizonDevice
    进行运行时硬件检测(物理 Quest 设备识别)。使用
    isHorizonBuild
    进行构建时功能控制(判断编译了哪些原生代码)。
  • 不支持 Expo Go。 你必须通过
    npx expo prebuild
    使用自定义开发构建。

Official References

官方参考

Always webfetch the raw markdown (
raw.githubusercontent.com/...
) if the HTML view does not render the source; the raw URL is the source of truth.
TopicOfficial source
Repo overview and package listexpo-horizon README
Install, config plugin options, runtime API, native module accessexpo-horizon-core README
Location migration, limitations, feature support matrixexpo-horizon-location README
Push notifications migration, token types, feature support matrixexpo-horizon-notifications README
Example app wiring for all three packagesexpo-horizon example README
Panel sizing guidelines (dp values, orientation, letterboxing)Meta Panel Sizing
Meta Horizon Store manifest checklist for publishingPublish Mobile Manifest
Device management, casting, sideloading, ADBMeta Quest Developer Hub
Server-side push delivery via Meta's push serviceHorizon OS push notifications
如果 HTML 视图无法渲染源文件,请始终联网获取原始 markdown(
raw.githubusercontent.com/...
);原始 URL 是权威来源。
主题官方来源
仓库概览和包列表expo-horizon README
安装、配置插件选项、运行时 API、原生模块访问expo-horizon-core README
定位迁移、限制、功能支持矩阵expo-horizon-location README
推送通知迁移、令牌类型、功能支持矩阵expo-horizon-notifications README
三个包的示例应用配置expo-horizon example README
面板尺寸指南(dp 值、方向、黑边处理)Meta Panel Sizing
Meta Horizon Store 发布清单检查项Publish Mobile Manifest
设备管理、投屏、侧载、ADBMeta Quest Developer Hub
通过 Meta 推送服务实现服务端推送Horizon OS push notifications