Expo SDK 58 đã chính thức ra mắt, đi kèm React Native 0.88 và được xây dựng cho iOS 27. Đây là một bản cập nhật lớn: app sẵn sàng cho iPhone Duo ngay ngày đầu, có thể tích hợp với Siri, và Expo có hẳn một CLI mới dành riêng cho AI agent.

Nếu bạn chỉ có một phút, đây là những điều đáng nhớ nhất:

  • iOS 27 và iPhone Duo: chuyển sang scene-based life cycle, thêm gói expo-hinge và component ArrangementView cho máy gập.
  • Xcode 27 Device Hub: Expo CLI hỗ trợ sẵn, kèm plugin expo-device-hub điều khiển simulator ngay trong trình duyệt.
  • App Intents: gói expo-app-intents đưa app vào Siri, Shortcuts, Spotlight và Apple Intelligence.
  • AI agent: @expo/agent-cli là CLI mới, thiết kế để coding agent sử dụng.
  • Hiệu năng: build Android sạch nhanh gần gấp đôi, Expo Modules 2.0 bước vào beta.
  • Expo Router: data loaders, SSR, middleware và native tabs đã ổn định.

Lưu ý: Expo Go cho SDK 58 sẽ lên App Store và Google Play trong vài tuần tới. Trong thời gian đó, hãy dùng development build để thử SDK mới.

iOS 27 và iPhone Duo: sẵn sàng từ ngày đầu

iOS 27 bắt buộc app dùng UIKit scene-based life cycle, và mọi app iPhone đều có thể bị thay đổi kích thước cửa sổ. SDK 58 đã chuyển sang scene life cycle, nên app build bằng iOS 27 SDK sẽ khởi động đúng.

Thay đổi này ảnh hưởng đến cách các callback của app delegate được gọi. Nếu bạn có tùy biến AppDelegate.swift, hãy đọc hướng dẫn migration về scene life cycle trước khi nâng cấp.

Hai điểm dễ gây bất ngờ trên iOS 27:

  • requireFullScreen không còn giúp app tránh bị resize.
  • ScreenOrientation.lockAsync có thể không có tác dụng khi app đang ở chế độ resizable.

Chuẩn bị cho iPhone Duo

Trên iPhone Duo, iOS dời phần lớn thanh điều hướng và nút điều khiển vào một thanh dọc ở cạnh trái hoặc phải. App cũng phải xử lý việc kích thước cửa sổ thay đổi khi người dùng gập máy hoặc chia đôi màn hình trong.

Những việc nên làm trước:

  • Điều hướng: dùng Stack và NativeTabs của Expo Router, hoặc view SwiftUI qua <Host> của Expo UI. Chúng dùng thanh điều hướng của hệ thống, nên iOS tự đặt đúng vị trí trên cả hai màn hình.
  • Safe area: có thể cần thêm <SafeAreaView> ở nhiều chỗ hơn, kể cả bên trong ScrollView, để xử lý lề ngang.
  • Bố cục: Expo UI có ArrangementView, container SwiftUI mà Apple khuyên dùng cho iPhone Duo. Nó đặt hai view cạnh nhau hoặc chồng lên nhau tùy theo nếp gập và tư thế máy.
  • Popover: tác giả module có helper SceneGeometry.anchorPopover trong expo-modules-core để neo popover đúng chỗ.

Cập nhật thư viện safe area lên bản mới nhất:

npx expo install react-native-safe-area-context

expo-hinge: đọc trạng thái bản lề

Gói mới expo-hinge cho biết góc bản lề và máy đang đóng, mở một phần hay mở hoàn toàn, qua useHinge(), getHinge() và addHingeListener(). Gói chạy trên iOS 27.1 trở lên và trên máy gập Android 11 trở lên.

Trong SwiftUI có modifier onHingeChange cho từng view. Apple khuyên chỉ dùng trạng thái bản lề cho tương tác và hiệu ứng, ví dụ nút chụp ảnh khi đặt máy kiểu "tabletop", chứ không dùng để quyết định bố cục.

Device Hub: điều khiển simulator ngay trong trình duyệt

Xcode 27 thay Simulator.app bằng Device Hub. Expo CLI tự nhận ra Device Hub, khởi động simulator thông qua nó và đưa đúng thiết bị lên trước bằng deep link devices://.

Nếu không muốn rời editor, bạn có thể dùng plugin dev tools mới expo-device-hub. Nó biến trình duyệt thành một bảng điều khiển thiết bị cục bộ:

  • Xem trực tiếp mọi iOS simulator và Android emulator, chạm, vuốt và gõ phím ngay trên đó.
  • Bật và tắt thiết bị.
  • Theo dõi biểu đồ CPU, bộ nhớ và mạng.
  • Chuyển nhanh giao diện sáng/tối, Liquid Glass, cỡ chữ và các cài đặt trợ năng.

Cài đặt rất đơn giản, sau đó link mở bảng điều khiển sẽ hiện trong terminal:

npx expo install expo-device-hub
npx expo start

Expo cũng đang chuẩn bị EAS Simulator, dịch vụ simulator chạy trên cloud. Khi ra mắt, bạn sẽ có trải nghiệm tương tự mà không cần cài Xcode hay Android Studio trên máy.

App Intents: đưa app vào Siri và Apple Intelligence

Gói expo-app-intents (đang alpha) đưa các hành động và nội dung của app ra Siri, Shortcuts, Spotlight và Apple Intelligence. Bạn khai báo intent bằng Swift inline module trong thư mục app-intents, để Apple tìm thấy chúng lúc build.

Khi người dùng gọi intent, lời gọi được chuyển về JavaScript để xử lý. Dữ liệu entity cũng được cập nhật từ phía JavaScript.

Ví dụ: một app checklist có thể để Siri liệt kê các việc cần làm, hỏi người dùng muốn hoàn thành việc nào, rồi chạy intent trong app.

Ngoài ra, gói còn hỗ trợ:

  • Đánh chỉ mục nội dung cho Spotlight.
  • Intent donation để hệ thống đưa ra gợi ý.
  • Nhận diện nội dung đang hiển thị trên màn hình bằng appEntityIdentifier() (Expo UI) hoặc AppEntityView (React Native). Tính năng này cần iOS 18.4 và Xcode 27 trở lên.

Khởi tạo trong dự án:

npx expo-app-intents init

Nếu bạn dùng coding agent, cài thêm agent skill của gói:

npx @expo/agent-cli@latest install expo-app-intents

Phiên bản sắp tới sẽ cho phép chạy task của expo-task-manager từ intent handler. Khi đó app có thể xử lý bằng JavaScript và trả kết quả cho Siri mà không cần mở app.

Expo UI: danh sách lớn, điều hướng native và nhiều component mới

Expo UI, thư viện cho phép dùng SwiftUI và Jetpack Compose từ React Native, có nhiều nâng cấp nhất trong bản này.

Danh sách có virtualize và tái sử dụng hàng

List, LazyVStack, LazyHStack trên iOS và LazyColumn, LazyRow trên Android giờ chỉ render các hàng gần vùng đang hiển thị. Khi cuộn, các hàng được tái sử dụng thay vì tạo mới.

Cách dùng: truyền dữ liệu qua data, khóa duy nhất qua keyExtractor, và một hàm children trả về từng hàng. Với SwiftUI dùng List.ForEach, LazyVStack.ForEach; với Compose dùng LazyColumn.Items, LazyRow.Items.

Điều hướng native trên iOS

  • Thêm NavigationStack, Toolbar, NavigationLink, NavigationDestination và modifier navigationTitle.
  • NavigationSplitView hỗ trợ bố cục hai và ba cột.
  • ToolbarItem nhận thêm placement và visibilityPriority.

SwiftUI và Jetpack Compose

  • SwiftUI: các modifier như background, tint, border, foregroundStyle giờ nhận mọi ShapeStyle, nên có thể tô bằng material hoặc gradient. Thêm component Background và nhiều modifier mới như scrollClipDisabled, geometryGroup, alignmentGuide.
  • Jetpack Compose: thêm Image, DateRangePicker, DateRangePickerDialog và VerticalSlider. DropdownMenu có cornerRadius, shadowElevation; RadioButton có enabled, colors.
  • Component dùng chung: BottomSheet thêm contentPadding, containerColor, contentColor. Trên web, BottomSheet bỏ thư viện vaul và dùng thẻ <dialog> của HTML.

React Native view bên trong Expo UI

View nằm trong <RNHostView> giờ được đo đúng vị trí mà SwiftUI hay Compose đặt chúng. Nhờ đó measure() trả về giá trị chính xác, và cảm ứng hoạt động bên trong BottomSheet, Popover, AlertDialog, DropdownMenu.

Tài liệu Expo UI cũng được làm mới: mọi component giờ có ảnh chụp ngay cạnh API, giúp bạn thấy trước component trông ra sao.

Phát triển cùng AI agent

Expo đang đầu tư nghiêm túc cho việc lập trình cùng AI agent, với hai thay đổi chính trong SDK 58.

Expo CLI "hiểu" agent hơn

Expo CLI có phần hướng dẫn (help) viết lại và hai lệnh thử nghiệm mới: npx expo command:ps và npx expo command:events. Chúng trả về thông tin dạng máy đọc được về các tiến trình Expo đang chạy và log sự kiện của chúng.

Nhờ vậy agent biết được trạng thái của các dev server, kể cả server do nó không tự khởi động. Hệ thống này thay cho cơ chế debug log cũ, nên output khi chạy với EXPO_DEBUG=1 sẽ khác trước.

@expo/agent-cli: CLI dành cho agent

Trước đây, agent thường phải học Expo bằng cách vấp lỗi. Ví dụ, nó mở app trong Expo Go, gặp lỗi vì native module không được hỗ trợ, rồi mới đoán ra là cần development build.

@expo/agent-cli (thử nghiệm) giải quyết việc này. Nó chạy trên nền Expo CLI, EAS CLI và expo-doctor, nên có thể thay hoàn toàn cho Expo CLI khi làm việc với agent. Mỗi lệnh đều in ra bước tiếp theo để agent biết cần làm gì.

Lệnh

Tác dụng

status

Cho biết app có chạy được trên Expo Go không mà không cần khởi động app

dev

Khởi động app bằng một lệnh, tự quyết định khi nào cần build và chọn cách nhanh nhất

smoke

Chạy dev, chụp màn hình rồi dừng, để kiểm tra máy đã sẵn sàng phát triển

skills:sync

Cài các agent skill đi kèm package trong node_modules

Bắt đầu bằng lệnh dưới đây, rồi dặn agent dùng @expo/agent-cli thay cho Expo CLI:

npx @expo/agent-cli@latest agents:setup

Nếu thích tên ngắn hơn, bạn có thể dùng alias expo-agent-cli. Một số package như expo-sqlite và expo-app-intents cũng đã có agent skill riêng, cài qua npx @expo/agent-cli@latest install <tên-package>.

Hiệu năng: build nhanh hơn, gọi native nhanh hơn

SDK 58 cải thiện tốc độ ở cả khâu build lẫn lúc app chạy. Dưới đây là các con số do Expo công bố:

Hạng mục

Trước

Sau

Build Android debug sạch, 4 ABI (Apple M3 Pro)

~80 s

~39 s

Cold bundle app lớn nhất (~5.500 file)

5,26 s (Babel)

2,46 s (Noxcturnal)

Gọi hàm không tham số trên Android

2.837 ns (TurboModule)

112 ns (Expo Modules 2.0)

Truyền mảng 20 object trên Android

149,8 µs (TurboModule)

10,7 µs (Expo Modules 2.0)

Phát event kèm object trên Android

8.080 ns (TurboModule)

963 ns (Expo Modules 2.0)

Build Android nhanh gần gấp đôi

Trước đây, mỗi lần build sạch, chạy CI hay EAS Build đều phải biên dịch phần C++ của expo-modules-core từ mã nguồn. Đây là bước tốn thời gian nhất khi build Android với Expo.

Giờ package npm đã kèm sẵn file .so cho cả 4 ABI, bản debug và release. Máy ít nhân, như runner CI, sẽ tiết kiệm được nhiều hơn. Muốn tự biên dịch như cũ, bật android.buildExpoModulesCoreFromSource qua expo-build-properties.

Gọi native module nhanh hơn, không cần sửa code

Trên iOS, lời gọi truyền hoặc trả về giá trị đơn giản rẻ hơn, truyền chuỗi dài nhanh hơn tới 3,8 lần, và promise tạo ra cũng nhẹ hơn. Bạn chỉ cần nâng lên SDK 58 là mọi Expo module đều nhanh hơn.

Expo Modules 2.0 bước vào beta

Thay vì mô tả module bằng DSL riêng, giờ bạn viết một class Swift hoặc Kotlin bình thường và gắn annotation cho các phương thức muốn đưa ra JavaScript. Cách viết này cũng hợp với AI agent, vì mô hình đã quen Swift và Kotlin.

Annotation cho phép sinh binding trực tiếp lúc build. Trên iOS, lời gọi đồng bộ nhanh hơn DSL từ 2,5 đến 5,6 lần (đo trên iPhone 16 Pro, bản Release). Trên Android, bản 2.0 nhanh hơn cả bản 1.0 lẫn TurboModule ở toàn bộ 12 bài đo.

Fingerprint mặc định là balanced

Nâng version trong app.json hay thay đổi file trong node_modules không còn làm đổi fingerprint. Native module được hash theo tên và version thay vì theo từng file, nên bớt được nhiều lần build native không cần thiết.

Thử nghiệm: Noxcturnal, bộ transform viết bằng Rust

Noxcturnal được xây trên oxc, thay cho Babel khi Metro bundle lần đầu. Thử nội bộ cho thấy transform từng module nhanh khoảng 3 lần và cold bundle nhanh khoảng 2 lần.

Bật bằng experiments.noxcturnalTransformWorker trong app config. Hiện nó chỉ hoạt động khi dự án không có custom transform worker hay plugin Babel riêng. File cần transform của Reanimated hoặc Worklets vẫn đi qua Babel.

Chuẩn bị cho Swift Package Manager

React Native 0.87 bắt đầu hỗ trợ thử nghiệm Swift Package Manager thay cho CocoaPods. SDK 58 tái cấu trúc mã iOS của Expo để sẵn sàng cho việc này, kèm bản preview của plugin autolinking. CocoaPods vẫn là lựa chọn mặc định.

Expo Router: các tính năng lớn đã ổn định

Data loaders, server-side rendering và middleware đã ổn định, không cần bật cờ thử nghiệm nữa. Native tabs, toolbar và phần tích hợp điều hướng chuẩn cũng vậy.

Lõi điều hướng được viết lại để trạng thái luôn nhất quán và dễ suy luận hơn. Expo Router cũng bỏ phần lớn API React Navigation từng fork về. Đa số app sẽ không nhận ra khác biệt, trừ khi bạn tự viết navigator hoặc import từ expo-router/react-navigation.

Những thay đổi đáng chú ý khác:

  • Cache cho loader: dựa vào HTTP cache của nền tảng. Đặt header Cache-Control trong loader để quyết định client có dùng lại dữ liệu đã cache hay không.
  • Server output mặc định: nếu chưa đặt web.output, dự án sẽ dùng "server" và render HTML theo từng request. Muốn giữ cách cũ của SDK 57, đặt web.output là "static" và bật tùy chọn apiRoutes của config plugin.
  • Async routes trên web mặc định: mỗi route được tách thành chunk JavaScript riêng, chỉ tải khi cần. Tắt bằng asyncRoutes: { web: false }.
  • React transitions: giữ màn hình hiện tại trong lúc màn hình kế tiếp đang tải, và preload ở nền.
  • React activities: đặt activityEnabled để ẩn nội dung không hoạt động mà vẫn giữ state.
  • Custom router: dùng extendRouter hoặc extendRouterActions để mở rộng router có sẵn.
  • Error boundary theo màn hình: hiển thị giao diện dự phòng khi một màn hình lỗi, trong khi navigator vẫn chạy.
  • Safe area trên web: dùng CSS env(safe-area-inset-*) thay cho react-native-safe-area-context.
  • Analytics: API PerformanceObserver thử nghiệm để theo dõi màn hình đang được focus.

Hãy đọc kỹ migration guide của Expo Router khi nâng cấp, nhất là nếu bạn dùng custom navigator.

Những thay đổi đáng chú ý khác

Expo Observe: theo dõi app ngoài production

Expo Observe ghi lại hiệu năng khởi động và điều hướng, lỗi JavaScript và crash native từ thiết bị người dùng. Nó so sánh từng bản phát hành với các bản trước để bạn phát hiện sớm khi có gì đó tệ đi.

SDK 58 thêm tùy chọn errorHandlingEnabled để tắt tự ghi lỗi JS, và Observe.clientId để đối chiếu với công cụ khác. AppMetrics bị deprecate, thay bằng Observe.

Widget màn hình chính trên Android

expo-widgets giờ hỗ trợ Android. Widget chạy JS bundle riêng trên một runtime Hermes độc lập, có tương tác và Material Colors. Trên iOS, Live Activities thêm staleDate và cho phép đổi cấu hình widget lúc chạy.

Tunnel mới, không cần ngrok

Expo CLI có tunnel mới dựa trên @expo/ws-tunnel, kết nối nhanh và ổn định hơn. Bạn cần đăng nhập tài khoản Expo (expo login) để dùng. Tunnel ngrok cũ vẫn còn qua --tunnel ngrok.

npx expo start --tunnel

Development build cũng kết nối ổn định hơn khi dev server nằm sau proxy, trên máy từ xa hay qua tunnel, vì manifest giờ dùng URL tương đối.

Tích hợp PostHog bằng một lệnh

Lệnh eas integrations:posthog:connect tạo hoặc liên kết dự án PostHog, cài SDK và ghi biến môi trường. Mỗi event sẽ biết nó đến từ bản EAS Update nào. Đây là tính năng của EAS CLI nên dùng được với mọi SDK.

Launch URL thống nhất

Development build và Expo Go giờ dùng chung một quy ước launch URL. Mọi query parameter bắt đầu bằng __expo_ là lệnh cho launcher, nên không còn xung đột với deep link của app. Khi chạy CI, test end-to-end hay agent, đặt EXPO_NO_DEV_MENU=1 để ẩn onboarding, nút Tools và dev menu.

Các package khác

Package

Có gì mới

expo-camera

CameraView.scanDocumentAsync để quét tài liệu nhiều trang trên Android và iOS

expo-file-system

File.digest() tính hash MD5/SHA; File.preview() để xem trước file bằng giao diện native

expo-audio

Điều khiển trên màn hình khóa kèm playlist; ghi âm ra file WAV/PCM

expo-image

transition.skipOnCacheHit; prop svgVariables để đổi màu từng phần của SVG

expo-font

Hỗ trợ variable fonts trên Android, iOS và web

expo-notifications

Chạy chung được với thư viện push khác trên iOS; delivery: 'alarmClock' cho báo thức quan trọng trên Android

expo-location

API mới thử nghiệm tại expo-location/next; geofencing trên iOS không còn cần background mode

expo-sqlite

interruptSync() để hủy thao tác đang chạy; configureWeb() để hỗ trợ full-text search trên web

expo-secure-store

requireConfirmation trên Android cho đọc/ghi có xác thực

expo-updates

updates.maxUpdatesToKeep để đặt số bản update lưu trên máy

Breaking changes cần lưu ý

Bảng dưới đây liệt kê những thay đổi dễ ảnh hưởng đến app của bạn nhất. Danh sách đầy đủ nằm trong changelog gốc.

Hạng mục

Thay đổi

Cách xử lý

TypeScript của React Native

Strict TypeScript API thành mặc định; import sâu từ react-native/Libraries/* báo lỗi type; ref dùng kiểu như ViewInstance

Làm theo migration guide. Tạm hoãn bằng customConditions trong tsconfig.json, nhưng lựa chọn này sẽ bị bỏ sau React Native 0.89

API React Native bị xóa

InteractionManager, prop animated của Modal, một số prop của StatusBar; ImageBackground bị deprecate

Dùng requestIdleCallback; thay ImageBackground bằng View chứa Image đặt tuyệt đối

macOS

SDK 58 chưa hỗ trợ macOS

Chờ react-native-macos cập nhật

iOS scene life cycle

prebuild sinh SceneDelegate.swift; UIWindow do scene delegate tạo

Ai tự quản lý thư mục ios hoặc override app delegate cần đọc migration guide

NODE_ENV

Expo CLI tự đặt development hoặc production; file .env.test không còn được nạp

Sắp xếp lại file .env theo hai chế độ này

R8 trên Android

Bật mặc định cho bản release

Thêm keep rule cho thư viện dùng reflection, hoặc tắt qua expo-build-properties

Expo Router

Lõi điều hướng mới; mặc định server output; @expo/ui và expo-symbols thành optional peer dependency

Đọc migration guide của Expo Router

expo-file-system

File.write() giờ là async

Dùng File.writeSync() nếu cần hành vi cũ

expo-sqlite

Bỏ hỗ trợ libSQL

Xóa code dùng syncLibSQL() và libSQLOptions

expo-notifications

Thông báo khi app đang mở được hiển thị mặc định

Kiểm tra lại setNotificationHandler

@expo/ui

<Host> trên iOS căn nội dung lên trên; HStack/VStack dùng spacing mặc định của hệ thống

Truyền spacing={0} để giữ bố cục cũ

expo login

Mặc định đăng nhập qua trình duyệt

Dùng --no-browser cho cách cũ

Node.js

Cần 22.13+, 24.3+ hoặc 26+; không hỗ trợ bản số lẻ

Nâng cấp Node.js trước khi nâng SDK

Nâng cấp từ SDK 57

Nếu dùng Claude Code hoặc công cụ tương tự, bạn có thể nhờ skill expo-upgrade của Expo hỗ trợ. Nếu làm thủ công, hãy đi theo các bước sau:

  1. Nâng mọi dependency lên bản khớp với SDK 58:
    npx expo install expo@^58.0.0 --fix
  2. Kiểm tra các lỗi đã biết bằng Expo Doctor:
    npx expo-doctor@latest
  3. Đối chiếu phần breaking changes ở trên và changelog đầy đủ.
  4. Nếu tùy biến AppDelegate.swift hoặc tự quản lý thư mục ios, đọc hướng dẫn migration về scene life cycle.
  5. Nếu dùng Continuous Native Generation (CNG), xóa thư mục android và ios để chúng được tạo lại ở lần build sau.
  6. Nếu không dùng CNG, chạy npx pod-install và áp dụng thay đổi từ Native project upgrade helper.
  7. Nếu dùng expo-dev-client, tạo development build mới sau khi nâng cấp.

Tạo dự án mới với SDK 58

Trong lúc chờ Expo Go mới, npx create-expo-app vẫn tạo dự án SDK 57. Để bắt đầu với SDK 58 ngay, dùng template riêng và chạy bằng development build:

npx create-expo-app --template default@sdk-58

Expo Go mới sẽ chỉ hỗ trợ SDK 58. Nếu đang dùng Expo Go từ store, bạn cần nâng cấp dự án để tiếp tục dùng được.

Lời kết

SDK 58 cho thấy Expo đang đi theo ba hướng cùng lúc. Một là bám sát nền tảng mới của Apple như iOS 27, iPhone Duo và Siri. Hai là làm app nhanh hơn, từ lúc build đến lúc chạy. Ba là biến AI agent thành người dùng hạng nhất của bộ công cụ.

Nếu đang phát triển app cho iOS, bạn nên nâng cấp sớm để kịp chuẩn bị cho iOS 27 và iPhone Duo. Hãy dành thời gian cho Strict TypeScript API của React Native, vì lựa chọn tạm hoãn sẽ sớm bị bỏ.