diff --git a/docs/components/plugins/api/APIStaticData.ts b/docs/components/plugins/api/APIStaticData.ts index 67ee23ae46dc3d..a6fe25b74f6b8c 100644 --- a/docs/components/plugins/api/APIStaticData.ts +++ b/docs/components/plugins/api/APIStaticData.ts @@ -375,6 +375,36 @@ export const sdkVersionHardcodedTypeLinks: Record **warning** **[Deprecated](/more/release-statuses/#deprecated):** The `expo-contacts` library is being replaced by a new version in [`expo-contacts/next`](contacts-next.mdx). The current API is not receiving patches and will be removed in an upcoming release. + `expo-contacts` provides access to the device's system contacts, allowing you to get contact information as well as adding, editing, or removing contacts. On iOS, contacts have a multi-layered grouping system that you can also access through this API. diff --git a/docs/pages/versions/unversioned/sdk/media-library-next.mdx b/docs/pages/versions/unversioned/sdk/media-library-next.mdx index 5e741eccff173b..2044588566639c 100644 --- a/docs/pages/versions/unversioned/sdk/media-library-next.mdx +++ b/docs/pages/versions/unversioned/sdk/media-library-next.mdx @@ -5,7 +5,6 @@ sourceCodeUrl: 'https://github.com/expo/expo/tree/main/packages/expo-media-libra packageName: 'expo-media-library' iconUrl: '/static/images/packages/expo-media-library.png' platforms: ['android', 'ios', 'tvos', 'expo-go'] -isNew: true --- import APISection from '~/components/plugins/APISection'; diff --git a/docs/pages/versions/unversioned/sdk/media-library.mdx b/docs/pages/versions/unversioned/sdk/media-library.mdx index 15ba7f4ce8b5a8..429695228b3de0 100644 --- a/docs/pages/versions/unversioned/sdk/media-library.mdx +++ b/docs/pages/versions/unversioned/sdk/media-library.mdx @@ -5,6 +5,7 @@ sourceCodeUrl: 'https://github.com/expo/expo/tree/main/packages/expo-media-libra packageName: 'expo-media-library' iconUrl: '/static/images/packages/expo-media-library.png' platforms: ['android', 'ios', 'tvos', 'expo-go'] +isDeprecated: true --- import APISection from '~/components/plugins/APISection'; @@ -17,6 +18,8 @@ import { } from '~/ui/components/ConfigSection'; import { SnackInline } from '~/ui/components/Snippet'; +> **warning** **[Deprecated](/more/release-statuses/#deprecated):** The `expo-media-library` library is being replaced by a new version in [`expo-media-library/next`](media-library-next.mdx). The current API is not receiving patches and will be removed in an upcoming release. + `expo-media-library` provides access to the user's media library, allowing them to access their existing images and videos from your app, as well as save new ones. You can also subscribe to any updates made to the user's media library. > **warning** Android allows full access to the media library (which is the purpose of this package) only for applications needing broad access to photos. See [Details on Google Play's Photo and Video Permissions policy](https://support.google.com/googleplay/android-developer/answer/14115180). diff --git a/docs/pages/versions/unversioned/sdk/router/split-view.mdx b/docs/pages/versions/unversioned/sdk/router/split-view.mdx index 3bfba08d64517a..ee1465deabcfb3 100644 --- a/docs/pages/versions/unversioned/sdk/router/split-view.mdx +++ b/docs/pages/versions/unversioned/sdk/router/split-view.mdx @@ -6,7 +6,6 @@ sourceCodeUrl: 'https://github.com/expo/expo/tree/main/packages/expo-router' packageName: 'expo-router' platforms: ['ios'] isAlpha: true -isNew: true --- import { BookOpen02Icon } from '@expo/styleguide-icons/outline/BookOpen02Icon'; diff --git a/docs/pages/versions/unversioned/sdk/widgets.mdx b/docs/pages/versions/unversioned/sdk/widgets.mdx index 408e5277db55fd..9711e76da7d773 100644 --- a/docs/pages/versions/unversioned/sdk/widgets.mdx +++ b/docs/pages/versions/unversioned/sdk/widgets.mdx @@ -4,14 +4,13 @@ description: A library to build iOS home screen widgets and Live Activities usin sourceCodeUrl: 'https://github.com/expo/expo/tree/main/packages/expo-widgets' packageName: 'expo-widgets' platforms: ['ios'] -isAlpha: true --- import APISection from '~/components/plugins/APISection'; import { APIInstallSection } from '~/components/plugins/InstallSection'; import { ConfigPluginExample, ConfigPluginProperties } from '~/ui/components/ConfigSection'; -> **important** This library is currently in [alpha](/more/release-statuses/#alpha) and subject to breaking changes. It is not available in the Expo Go app, use [development builds](/develop/development-builds/introduction) to try it out. +> **important** This library is not available in the Expo Go app — use [development builds](/develop/development-builds/introduction) to try it out. `expo-widgets` enables the creation of iOS home screen widgets and Live Activities using Expo UI components, without writing native code. It provides a simple API for creating and updating widgets timeline, as well as starting and managing Live Activities. Layout can be built using [`expo/ui`](/versions/latest/sdk/ui/swift-ui/) components and modifiers. diff --git a/docs/pages/versions/v56.0.0/config/app.mdx b/docs/pages/versions/v56.0.0/config/app.mdx new file mode 100644 index 00000000000000..8ac8ec24e128f2 --- /dev/null +++ b/docs/pages/versions/v56.0.0/config/app.mdx @@ -0,0 +1,24 @@ +--- +title: app.json / app.config.js +description: A reference of available properties in Expo app config. +maxHeadingDepth: 5 +--- + +import { BookOpen02Icon } from '@expo/styleguide-icons/outline/BookOpen02Icon'; + +import schema from '~/public/static/schemas/v56.0.0/app-config-schema.json'; +import AppConfigSchemaTable from '~/ui/components/AppConfigSchemaTable'; +import { BoxLink } from '~/ui/components/BoxLink'; + +The following is a list of properties that are available for you under the `"expo"` key in **app.json** or **app.config.json**. These properties can be passed to the top level object of **app.config.js** or **app.config.ts**. + + + +## Properties + + diff --git a/docs/pages/versions/v56.0.0/config/babel.mdx b/docs/pages/versions/v56.0.0/config/babel.mdx new file mode 100644 index 00000000000000..af29a639094238 --- /dev/null +++ b/docs/pages/versions/v56.0.0/config/babel.mdx @@ -0,0 +1,37 @@ +--- +title: babel.config.js +description: A reference for Babel configuration file. +--- + +import { Terminal } from '~/ui/components/Snippet'; + +Babel is used as the JavaScript compiler to transform modern JavaScript (ES6+) into a version compatible with the JavaScript engine on mobile devices. + +Each new Expo project created using `npx create-expo-app` configures Babel automatically and uses [`babel-preset-expo`](https://github.com/expo/expo/tree/main/packages/babel-preset-expo) as the default preset. There is no need to create a **babel.config.js** file unless you need to customize the Babel configuration. + +## Create babel.config.js + +If your project requires a custom Babel configuration, you need to create the **babel.config.js** file in your project by following the steps below: + +1. Navigate to the root of your project and run the following command inside a terminal. This will generate a **babel.config.js** file in the root of your project. + + + +2. The **babel.config.js** file contains the following default configuration: + +```js babel.config.js +module.exports = function (api) { + api.cache(true); + return { + presets: ['babel-preset-expo'], + }; +}; +``` + +3. If you make a change to the **babel.config.js** file, you need to restart the Metro bundler to apply the changes and use `--clear` option from Expo CLI to clear the Metro bundler cache: + + + +## babel-preset-expo + +[`babel-preset-expo`](https://github.com/expo/expo/tree/main/packages/babel-preset-expo) is the default preset used in Expo projects. It extends the default React Native preset (`@react-native/babel-preset`) and adds support for decorators, tree-shaking web libraries, and loading font icons. diff --git a/docs/pages/versions/v56.0.0/config/metro.mdx b/docs/pages/versions/v56.0.0/config/metro.mdx new file mode 100644 index 00000000000000..7f0764595ee5e5 --- /dev/null +++ b/docs/pages/versions/v56.0.0/config/metro.mdx @@ -0,0 +1,706 @@ +--- +title: metro.config.js +description: A reference of available configurations in Metro. +--- + +import { BookOpen02Icon } from '@expo/styleguide-icons/outline/BookOpen02Icon'; + +import { BoxLink } from '~/ui/components/BoxLink'; +import { FileTree } from '~/ui/components/FileTree'; +import { DiffBlock, Terminal } from '~/ui/components/Snippet'; + +See more information about **metro.config.js** in the [customizing Metro guide](/guides/customizing-metro/). + +## Environment variables + +Expo CLI can load environment variables from **.env** files. Learn more about how to use environment variables in Expo CLI in the [environment variables guide](/guides/environment-variables/). + +EAS CLI uses a different mechanism for environment variables, except when it invokes Expo CLI for compiling and bundling. Learn more about [environment variables in EAS](/eas/environment-variables/). + +If you are migrating an older project, then you should ignore local env files by adding the following to your **.gitignore**: + +```sh .gitignore +# local env files +.env*.local +``` + +### Disabling dotenv files + +Dotenv file loading can be fully disabled in Expo CLI by enabling the `EXPO_NO_DOTENV` environment variable, before invoking any Expo CLI command. + + + +### Disabling `EXPO_PUBLIC_`-prefixed client environment variables + +Environment variables prefixed with `EXPO_PUBLIC_` will be exposed to the app at build-time. For example, `EXPO_PUBLIC_API_KEY` will be available as `process.env.EXPO_PUBLIC_API_KEY`. + +Client environment variable inlining can be disabled with the environment variable `EXPO_NO_CLIENT_ENV_VARS=1`, this must be defined before any bundling is performed. + + + +## CSS + +> **info** CSS support is under development and currently only works on web. + +Expo supports CSS in your project. You can import CSS files from any component. CSS Modules are also supported. + +CSS support is enabled by default. You can disable the feature by setting `isCSSEnabled` in the Metro config. + +```js metro.config.js +/** @type {import('expo/metro-config').MetroConfig} */ +const config = getDefaultConfig(__dirname, { + // Disable CSS support. + isCSSEnabled: false, +}); +``` + +### Global CSS + +> **warning** Global styles are web-only, usage will cause your application to diverge visually on native. + +You can import a CSS file from any component. The CSS will be applied to the entire page. + +Here, we'll define a global style for the class name `.container`: + +```css styles.css +.container { + background-color: red; +} +``` + +We can then use the class name in our component by importing the stylesheet and using `.container`: + +```jsx App.js|collapseHeight=470 +import './styles.css'; +import { View } from 'react-native'; + +export default function App() { + return ( + <> + {/* Use `className` to assign the style with React DOM components. */} +
Hello World
+ + {/* Use `style` with the following syntax to append class names in React Native for web. */} + + Hello World + + + ); +} +``` + +You can also import stylesheets that are vendored in libraries, just like you would any node module: + +```js index.js +// Applies the styles app-wide. +import 'emoji-mart/css/emoji-mart.css'; +``` + +- On native, all global stylesheets are automatically ignored. +- Hot reloading is supported for global stylesheets, simply save the file and the changes will be applied. + +> **warning** When using Expo Router, always import global CSS in your root **\_layout.tsx**. Expo Router traverses the dependency graph starting from the root layout. Importing CSS in a nested layout causes **node_modules** CSS to load before your custom styles, which can break your intended style order. + +### CSS Modules + +> **warning** CSS Modules for native are under development and currently only work on web. + +CSS Modules are a way to scope CSS to a specific component. This is useful for avoiding naming collisions and for ensuring that styles are only applied to the intended component. + +In Expo, CSS Modules are defined by creating a file with the `.module.css` extension. The file can be imported from any component. The exported value is an object with the class names as keys and the web-only scoped names as the values. The import `unstable_styles` can be used to access `react-native-web`-safe styles. + +CSS Modules support platform extensions to allow you to define different styles for different platforms. For example, you can define a `module.ios.css` and `module.android.css` file to define styles for Android and iOS respectively. You'll need to import without the extension, for example: + + + +Flipping the extension, for example, `App.ios.module.css` will not work and result in a universal module named `App.ios.module`. + +> You cannot pass styles to the `className` prop of a React Native or React Native for web component. Instead, you must use the `style` prop. + +```jsx App.js|collapseHeight=470 +import styles, { unstable_styles } from './App.module.css'; + +export default function Page() { + return ( + <> + + Hello World + + Hello World + {/* Web-only usage: */} +

Hello World

+ + ); +} +``` + +```css App.module.css +.text { + color: red; +} +``` + +- On web, all CSS values are available. CSS is not processed or auto-prefixed like it is with the React Native Web `StyleSheet` API. You can use `postcss.config.js` to autoprefix your CSS. +- CSS Modules use [lightningcss](https://github.com/parcel-bundler/lightningcss) under the hood, check [the issues](https://github.com/parcel-bundler/lightningcss/issues) for unsupported features. + +### PostCSS + +[PostCSS](https://github.com/postcss/postcss) can be customized by adding a `postcss.config.json` file to the root of your project. This file should export a function that returns a PostCSS configuration object. For example: + +```json postcss.config.json +{ + "plugins": { + "tailwindcss": {} + } +} +``` + +Both `postcss.config.json` and `postcss.config.js` are supported, but `postcss.config.json` enables better caching. + +Expo CLI automatically handles CSS vendor prefixes with built-in support for [browserslist](https://browsersl.ist/). Avoid adding `autoprefixer` as this duplicates the functionality and slows down bundling. + +#### Resetting cache after updates + +Changing the Post CSS or `browserslist` config will require you to clear the Metro cache: + + + +### browserslist + +Expo has automatic [browserslist](https://browsersl.ist/) support via the Rust-based CSS parser. You can customize the CSS vendor prefixes and browser support by adding a **browserslist** field to your **package.json** file. For example: + +```json package.json +{ + "browserslist": [">0.2%", "not dead", "not op_mini all"] +} +``` + +### SASS + +Expo Metro has _partial_ support for SCSS/SASS. + +To setup, install the `sass` package in your project: + + + +Then, ensure [CSS is setup](#css) in the **metro.config.js** file. + +- When `sass` is installed, then modules without extensions will be resolved in the following order: `scss`, `sass`, `css`. +- Only use the intended syntax with `sass` files. +- Importing other files from inside a scss/sass file is not currently supported. + +### Tailwind + +> **info** Standard Tailwind CSS supports only web platform. For universal support, use a library such as [NativeWind](https://www.nativewind.dev/) or [Uniwind](https://uniwind.dev/), which allow creating styled React Native components with Tailwind CSS. + + + +## Extending the Babel transformer + +Expo's Metro config uses a custom `transformer.babelTransformerPath` value to ensure `expo-babel-preset` is always used and web/Node.js environments are supported. + +If you want to extend the Babel transformer, import the upstream transformer from `@expo/metro-config/babel-transformer` instead of `metro-react-native-babel-transformer`. For example: + +```js metro.transformer.js +const upstreamTransformer = require('@expo/metro-config/babel-transformer'); + +module.exports.transform = async ({ src, filename, options }) => { + // Do something custom for SVG files... + if (filename.endsWith('.svg')) { + src = '...'; + } + // Pass the source through the upstream Expo transformer. + return upstreamTransformer.transform({ src, filename, options }); +}; +``` + +## Custom resolving + +Expo CLI extends the default Metro resolver to add features like Web, Server, and tsconfig aliases support. You can similarly customize the default resolution behavior of Metro by chaining the `config.resolver.resolveRequest` function. + +```tsx metro.config.js|collapseHeight=470 +const { getDefaultConfig } = require('expo/metro-config'); + +/** @type {import('expo/metro-config').MetroConfig} */ +const config = getDefaultConfig(__dirname); + +config.resolver.resolveRequest = (context, moduleName, platform) => { + if (moduleName.startsWith('my-custom-resolver:')) { + // Logic to resolve the module name to a file path... + // NOTE: Throw an error if there is no resolution. + return { + filePath: 'path/to/file', + type: 'sourceFile', + }; + } + + // Ensure you call the default resolver. + return context.resolveRequest(context, moduleName, platform); +}; + +module.exports = config; +``` + +Unlike traditional bundlers, Metro shared the same resolver function across all platforms. As a result, you can mutate the resolution settings dynamically on each request with the `context` object. + +### Mocking modules + +If you want a module to be empty for a given platform, you can return a `type: 'empty'` object from the resolver. The following example will cause `lodash` to be empty on web: + +```ts metro.config.js +const { getDefaultConfig } = require('expo/metro-config'); + +/** @type {import('expo/metro-config').MetroConfig} */ +const config = getDefaultConfig(__dirname); + +config.resolver.resolveRequest = (context, moduleName, platform) => { + if (platform === 'web' && moduleName === 'lodash') { + return { + type: 'empty', + }; + } + + // Ensure you call the default resolver. + return context.resolveRequest(context, moduleName, platform); +}; + +module.exports = config; +``` + +This technique is equivalent to using empty externals in Webpack or Vite, but with the added benefit of being able to target specific platforms. + +### Virtual modules + +Metro doesn't support virtual modules at the moment. One technique you can use to obtain similar behavior is to create a module in the `node_modules/.cache/...` directory and redirect the resolution to that file. + +The following example will create a module at `node_modules/.cache/virtual/virtual-module.js` and redirect the resolution of `virtual:my-module` to that file: + +```ts metro.config.js +const path = require('path'); +const fs = require('fs'); + +const { getDefaultConfig } = require('expo/metro-config'); + +/** @type {import('expo/metro-config').MetroConfig} */ +const config = getDefaultConfig(__dirname); + +const virtualPath = path.resolve(__dirname, 'node_modules/.cache/virtual/virtual-module.js'); + +// Create the virtual module in a generated directory... +fs.mkdirSync(path.dirname(virtualPath), { recursive: true }); +fs.writeFileSync(virtualPath, 'export default "Hello World";'); + +config.resolver.resolveRequest = (context, moduleName, platform) => { + if (moduleName === 'virtual:my-module') { + return { + filePath: virtualPath, + type: 'sourceFile', + }; + } + + // Ensure you call the default resolver. + return context.resolveRequest(context, moduleName, platform); +}; + +module.exports = config; +``` + +This can be used to emulate `externals` with custom imports. For example, if you want to redirect `require('expo')` to something custom like `SystemJS.require('expo')`, you can create a virtual module that exports `SystemJS.require('expo')` and redirect the resolution of `expo` to that file. + +## Custom transforming + +> Transformations are heavily cached in Metro. If you update something, use the `--clear` flag to see updates. For example, `npx expo start --clear`. + +Metro doesn't have a very expressive plugin system for transforming files, instead opt to use the [**babel.config.js**](../config/babel/) and caller object to customize the transformation. + +```js babel.config.js +module.exports = function (api) { + // Get the platform that Expo CLI is transforming for. + const platform = api.caller(caller => (caller ? caller.platform : 'ios')); + + // Detect if the bundling operation is for Hermes engine or not, e.g. `'hermes'` | `undefined`. + const engine = api.caller(caller => (caller ? caller.engine : null)); + + // Is bundling for a server environment, e.g. API Routes. + const isServer = api.caller(caller => (caller ? caller.isServer : false)); + + // Is bundling for development or production. + const isDev = api.caller(caller => + caller + ? caller.isDev + : process.env.BABEL_ENV === 'development' || process.env.NODE_ENV === 'development' + ); + + // Ensure the config is not cached otherwise the platform will not be updated. + api.cache(false); + // You can alternatively provide a more robust CONFIG cache invalidation: + // api.cache.invalidate(() => platform); + + return { + presets: ['babel-preset-expo'], + plugins: [ + // Add a plugin based on the platform... + platform === 'web' && 'my-plugin', + + // Ensure you filter out falsy values. + ].filter(Boolean), + }; +}; +``` + +If the caller doesn't have `engine`, `platform`, `bundler`, and so on, then ensure you are using `@expo/metro-config/babel-transformer` for the transformer. If you're using a custom transformer then it may need to extend the Expo transformer. + +Always try to implement custom logic in the resolver if possible, caching is much simpler and easier to reason about. For example, if you need to remap an import, it's simpler and faster to resolve to a static file with the resolver than to parse all possible import methods and remap them with the transformer. + +Always use `babel-preset-expo` as the default Babel preset, this ensures the transformation is always compatible with Expo runtimes. `babel-preset-expo` uses all of the caller inputs internally to optimize for a given platform, engine, and environment. + +## Node.js built-ins + +When bundling for a server environment, Expo's Metro config automatically supports externalizing Node.js built-in modules (`fs`, `path`, `node:crypto`, and more) based on the current Node.js version. If the CLI is bundling for a browser environment, then built-ins will first check if the module is installed locally, then fallback on an empty shim. For example, if you install `path` for use in the browser, this can be used, otherwise, the module will automatically be skipped. + +## Environment settings + +> **info** These environment variables will not be defined in test environments. + +Expo's Metro config injects build settings that can be used in the client bundle via environment variables. All variables will be inlined and cannot be used dynamically. For example, `process.env["EXPO_BASE_URL"]` won't work. + +- `process.env.EXPO_BASE_URL` exposes the base URL defined in `experiments.baseUrl`. This is used in Expo Router to respect the production base URL for deployment. + +## Bundle splitting + +Expo CLI automatically splits web bundles into multiple chunks based on async imports in production. This feature requires `@expo/metro-runtime` to be installed and imported somewhere in the entry bundle (available by default in Expo Router). + +Shared dependencies of async bundles are merged into a single chunk to reduce the number of requests. For example, if you have two async bundles that import `lodash`, then the library is merged into a single initial chunk. + +The chunk splitting heuristic cannot be customized. For example: + + + +```js math.js +export function add(a, b) { + return a + b; +} +``` + +```js index.js +import '@expo/metro-runtime'; + +// This will be split into a separate chunk. +import('./math').then(math => { + console.log(math.add(1, 2)); +}); +``` + +When you run `npx expo export -p web`, the bundles are split into multiple files, and the entry bundle is added to the main HTML file. `@expo/metro-runtime` adds the runtime code that loads and evaluates the async bundles. + +## Source map debug ID + +If a bundle is exported with an external source map, a [**Debug ID**](https://sentry.engineering/blog/the-case-for-debug-ids) annotation will be added to the end of the file, along with a matching `debugId` in the source map for corresponding the files together. If no source maps are exported, or inline source maps are used then this annotation will not be added. + +```js +// + +//# debugId= +``` + +The associated `*.js.map` or `*.hbc.map` source map will be a JSON file containing an equivalent `debugId` property. The `debugId` will be injected before hermes bytecode generation to ensure matching in all cases. + +The `debugId` is a deterministic hash of the bundle's contents without the external bundle splitting references. This is the same value used to create a chunks filename but formatted as a UUID. For example, `431b98e2-c997-4975-a3d9-2987710abd44`. + +`@expo/metro-config` injects `debugId` during `npx expo export` and `npx expo export:embed`. Any additional optimization steps in `npx expo export:embed` like Hermes bytecode generation will need to have the `debugId` injected manually. + +## Metro require runtime + +You can optionally enable a custom Metro `require` implementation with the environment variable `EXPO_USE_METRO_REQUIRE=1`. This runtime has the following features: + +- String module IDs that are human-readable and make missing module errors easier to follow. +- Deterministic IDs that are the same between runs and across modules (required for React Server Components in development). +- Removed support for legacy RAM bundles. + +## Magic import comments + +> Available from SDK 52 on all platforms. + +Server environments such as Workers, and Node.js support import arbitrary files at runtime, so you may want to keep `import` syntax in-tact instead of using Metro's require system. You can opt-out dynamic imports with the `/* @metro-ignore */` comment in `import()` statements. + +```js +// Manually ensure `./my-module.js` is included in the correct spot relative to the module. +const myModule = await import(/* @metro-ignore */ './my-module.js'); +``` + +Expo CLI will skip the `./my-module.js` dependency and assume that the developer has manually added it to the output bundle. Internally, this is used for exporting custom server code that dynamically switches between files based on the request. Avoid using this syntax for native bundles since `import()` is generally not available in React Native with Hermes enabled. + +Many React libraries shipped the Webpack `/* webpackIgnore: true */` comment to achieve similar behavior. To bridge the gap, we've also added support for Webpack's comment but recommend using the Metro equivalent in your app. + +## ES Module resolution + +> This sections applies from SDK 53 on all platforms. + +Metro resolves ES Module `import` and CommonJS `require` with separate resolution strategies. + +Previously, Metro applied the classic Node.js module resolution strategy (which matches Node.js versions before v12), with some additions to support ES Modules. In this resolution strategy, Metro resolves modules from `node_modules`, JS files, optionally while omitting extensions, such as `.js`, and uses `package.json` fields such as `main`, `module`, and `react-native`. + +Now, with the modern ES Modules resolution strategy, Metro instead resolves modules from `node_modules`, then matches different `package.json` fields, such as `exports`, [a nested map of sub-paths a package exposes](https://nodejs.org/api/packages.html#conditional-exports), and `main`. + +Depending on how a package is imported, one of these two resolution strategies will be used. Typically, a file that is imported with `import` from a Node module (rather than `require`), will use the ES Modules resolution strategy, and fall back on regular classic Node.js resolution. A file that wasn't resolved with ES Modules resolution or has been imported with CommonJS `require` will use the classic resolution strategy. + +### `package.json:exports` + +When performing ES Modules resolution, Metro will look at the `package.json:exports` conditions map. This is a mapping of import subpaths and conditions to files in the Node module package. + +For example, a package that always exposes an **index.js** file, and matches Metro's classic CommonJS module resolution, may specify a map with the `default` condition. + +```json +{ + "exports": { + "default": "./index.js" + } +} +``` + +However, a package providing both a CommonJS and ES Modules entrypoint may provide a mapping with the `import` and `require` conditions. + +```json +{ + "exports": { + "import": "./index.mjs", + "require": "./index.cjs" + } +} +``` + +By default, Metro will match different conditions depending on the platform and whether the resolution has started from a CommonJS `require` call, or an ES Modules `import` statement and will change the condition accordingly. + +For native platforms, the condition `react-native` is added, for web exports, the `browser` condition is added, and for server exports (such as API routes or React Server functions), the `node`, `react-server`, and `workerd` conditions are added. These conditions aren't matched in the order they're defined in. Instead, they're matched against the order of properties in the `package.json:exports` map. + +TypeScript performs ES Module resolution separately from Metro and will also respect `package.json:exports` maps, when its `compilerOptions.moduleResolution` configuration option has either been set to `"bundler"` (which matches Metro's behavior more closely) or to `"node16"` / `"nodenext"`. TypeScript will however also match the `types` condition. As such, types may not resolve properly when a package doesn't put the `types` condition first in its exports map. + +Since an exports map may contain subpaths, a package import may not have to match a file in the package's modules folder any longer, but may be a "redirected" import. Importing `'package/submodule'` may match a different file than **node_modules/package/submodule.js** if it's specified in `package.json:exports`. + +```json +{ + "exports": { + ".": "./index.js", + "./submodule": "./submodule/submodule.js" + } +} +``` + +If you're encountering packages that are incompatible or unprepared for the new ES Modules resolution strategy, you may be able to resolve problems by patching its `package.json` file and add or correct its `package.json:exports` conditions map. However, it's also possible to prevent Metro from using `package.json:exports` maps in its resolution by disabling the `unstable_enablePackageExports` option. + +```js metro.config.js +const { getDefaultConfig } = require('expo/metro-config'); + +/** @type {import('expo/metro-config').MetroConfig} */ +const config = getDefaultConfig(__dirname); + +config.resolver.unstable_enablePackageExports = false; + +module.exports = config; +``` + +## Asset imports + +When assets are imported, a virtual module is created to represent the data required for importing the asset. + +On native platforms, an asset will be a numeric ID: `1`, `2`, `3`, and so on, which can be looked up using `require("@react-native/assets-registry/registry").getAssetByID()`. On web and server platforms, the asset will change depending on the file type. If the file is an image, then the asset will be `{ uri: string, width?: number, height?: number }`, otherwise the asset will be a `string` representing the remote URL for the asset. As of SDK 55, you can use `String(asset)` to get the public URL of any asset on web, this excludes React Server Component environments which cannot contain a `toString` function. + +The assets can be used as follows: + +```jsx +import { Image } from 'react-native'; + +import asset from './img.png'; + +function Demo() { + return ; +} +``` + +In API routes, you can always assume the type of the asset will not be a number: + +```js +import asset from './img.png'; + +export async function GET(req: Request) { + const ImageData = await fetch( + new URL( + // Access the asset URI. + asset.uri, + // Append to the current request URL origin. + req.url + ) + ).then(res => res.arrayBuffer()); + + return new Response(ImageData, { + headers: { + 'Content-Type': 'image/png', + }, + }); +} +``` + +## Web workers + +> **important** This feature is alpha and subject to breaking changes. + +```ts +new Worker(new URL('./worker', window.location.href)); +``` + +Expo Metro has experimental web worker support. This feature is currently web-only and does not work on native, usage on native will trigger an error "Property 'Worker' doesn't exist". + +Web workers can be used to offload work to a separate thread on web, allowing the main thread to remain responsive. This is useful for computationally expensive tasks, such as image processing, cryptography, or other tasks that would otherwise block the main thread. + +Workers can be generated inline using `Blob`, but sometimes you may want to leverage modern features like TypeScript or importing other modules. + +Web workers depend on Expo bundle splitting support, which means you need to either use Expo Router or install and import `@expo/metro-runtime`. You also cannot use the environment `EXPO_NO_METRO_LAZY=1` with web workers. + +Consider the following example of a worker that doubles a number: + +```ts worker.ts +self.onmessage = ({ data }) => { + const result = data * 2; // Example: double the number + self.postMessage(result); +}; +``` + +This worker file can be imported as a `Worker` in the main app: + +```ts +// worker is of type `Worker` +const worker = new Worker(new URL('./worker', window.location.href)); + +worker.onmessage = ({ data }) => { + console.log(`Worker responded: ${data}`); +}; + +worker.postMessage(5); +``` + +Behind the scenes, Expo CLI is generating code like this: + +```ts +const worker = new Worker( + new URL('/worker.bundle?platform=web&dev=true&etc', window.location.href) +); +``` + +The generated bundle URL changes based on development/production to ensure the worker is loaded and bundled correctly. Unlike traditional bundle splitting, a worker file needs to contain its own copy of all modules and cannot depend on common modules in the main bundle. + +The native API `Worker` is traditionally unavailable in React Native and not provided by the Expo SDK, so even though this bundling feature technically works for all platforms, it's only useful on web. You could theoretically write a native Expo module that polyfills the `Worker` API if you want to support native platforms too. Alternatively, you can use the "worklet" API in React Native Reanimated to offload work to a separate thread on native. + +Alternatively, you can import Workers using the public path by first putting a transformed JS file in the **public** directory, then referencing it in the worker import with a variable: + +```ts +// Will avoid the transform and use the public path directly. +const worker = new Worker('/worker.js'); + +// The variable breaks the transform causing the literal path to be used instead of the transformed path. +const path = '/worker.js'; + +const anotherWorker = new Worker(new URL(path, window.location.href)); +``` + +Using a variable in the `Worker` constructor is not supported for bundling. To inspect the internal URL, you may use the internal syntax `require.unstable_resolveWorker('./path/to/worker.js')` to get the URL fragment. + +## Existing React Native apps + +> This guide is versioned and will need to be revisited when upgrading/downgrading Expo. Alternatively, use [Expo Prebuild](/more/glossary-of-terms/#prebuild) for fully automated setup. + +Projects that don't use [Expo Prebuild](/more/glossary-of-terms/#prebuild) must configure native files to ensure the Expo Metro config is always used to bundle the project. + +{/* If this isn't done, then features like [aliases](/guides/typescript/#path-aliases-optional), [absolute imports](/guides/typescript/#absolute-imports-optional), asset hashing, and more will not work. */} + +These modifications are meant to replace `npx react-native bundle` and `npx react-native start` with `npx expo export:embed` and `npx expo start` respectively. + +### metro.config.js + +Ensure the **metro.config.js** extends `expo/metro-config`: + +```js metro.config.js +const { getDefaultConfig } = require('expo/metro-config'); + +const config = getDefaultConfig(__dirname); + +module.exports = config; +``` + +### `android/app/build.gradle` + +The Android **app/build.gradle** must be configured to use Expo CLI for production bundling. Modify the `react` config object: + + + +### `ios/.xcodeproj/project.pbxproj` + +In your **ios/<Project>.xcodeproj/project.pbxproj** file, replace the following scripts: + +#### "Start Packager" script + +Remove the **"Start Packager"** script. The dev server must be started with `npx expo` before/after running the app. + + + +#### "Bundle React Native code and images" script + + + +Alternatively, in the Xcode project, select the **"Bundle React Native code and images"** build phase and add the following modifications: + + + +> You can set `CLI_PATH`, `BUNDLE_COMMAND`, and `ENTRY_FILE` environment variables to overwrite these defaults. + +### Custom entry file + +By default, React Native only supports using a root `index.js` file as the entry file (or platform-specific variation like `index.ios.js`). Expo projects allow using any entry file, but this requires addition bare setup. + +#### Development + +Development mode entry files can be enabled by using the [`expo-dev-client`](../sdk/dev-client/) package. Alternatively you can add the following configuration: + + + + + +#### Production + +In your **ios/<Project>.xcodeproj/project.pbxproj** file, replace the **"Bundle React Native code and images"** script to set `$ENTRY_FILE` according using Metro: + + + +The Android **app/build.gradle** must be configured to use Metro module resolution to find the root entry file. Modify the `react` config object: + + diff --git a/docs/pages/versions/v56.0.0/config/package-json.mdx b/docs/pages/versions/v56.0.0/config/package-json.mdx new file mode 100644 index 00000000000000..0229c98fb267e9 --- /dev/null +++ b/docs/pages/versions/v56.0.0/config/package-json.mdx @@ -0,0 +1,105 @@ +--- +title: package.json +description: A reference for Expo-specific properties that can be used in the package.json file. +--- + +import { PaddedAPIBox } from '~/components/plugins/PaddedAPIBox'; + +**package.json** is a JSON file that contains the metadata for a JavaScript project. This is a reference to Expo-specific properties that can be used in the **package.json** file. + + + +## `install.exclude` + +The following commands perform a version check for the libraries installed in a project and give a warning when a library's version is different from the version recommended by Expo: + +- `npx expo start` and `npx expo-doctor` +- `npx expo install` (when installing a new version of that library or using `--check` or `--fix` options) + +By specifying the library under the `install.exclude` array in the **package.json** file, you can exclude it from the version checks: + +```json package.json +{ + "expo": { + "install": { + "exclude": ["expo-updates", "expo-splash-screen"] + } + } +} +``` + + + + + +## `autolinking` + +Allows configuring module resolution behavior by using `autolinking` property in **package.json**. + +For complete reference, see [Autolinking configuration](/modules/autolinking/#configuration). + + + + + +## `doctor` + +Allows configuring the behavior of the [`npx expo-doctor`](/develop/tools/#expo-doctor) command. + + + +### `reactNativeDirectoryCheck` + +By default, Expo Doctor validates your project's packages against the [React Native directory](https://reactnative.directory/). This check throws a warning with a list of packages that are not included in the React Native Directory. + +You can customize this check by adding the following configuration in your project's **package.json** file: + +```json package.json +{ + "expo": { + "doctor": { + "reactNativeDirectoryCheck": { + /* @info Use this option to disable/enable the check. */ + "enabled": true, + /* @end */ + /* @info Use this option to exclude specific packages. */ + "exclude": ["/foo/", "bar"], + /* @end */ + /* @info Use this option to disable/enable listing unknown packages. */ + "listUnknownPackages": true + /* @end */ + } + } + } +} +``` + +By default, the check is enabled and unknown packages are listed. + + + + + +### `appConfigFieldsNotSyncedCheck` + +Expo Doctor checks if your project includes native project directories such as **android** or **ios**. If these directories exist but are not listed in your **.gitignore** or [**.easignore**](/build-reference/easignore) files, Expo Doctor verifies the presence of an app config file. If this file exists, it means your project is configured to use [Prebuild](/more/glossary-of-terms/#prebuild). + +When the **android** or **ios** directories are present, EAS Build does not sync app config properties to the native projects. Expo Doctor throws a warning if these conditions are true. + +You can disable or enable this check by adding the following configuration to your project's **package.json** file: + +```json package.json +{ + "expo": { + "doctor": { + "appConfigFieldsNotSyncedCheck": { + "enabled": false + } + } + } +} +``` + + + + diff --git a/docs/pages/versions/v56.0.0/index.mdx b/docs/pages/versions/v56.0.0/index.mdx new file mode 100644 index 00000000000000..ae853ed6c0d700 --- /dev/null +++ b/docs/pages/versions/v56.0.0/index.mdx @@ -0,0 +1,131 @@ +--- +title: Expo SDK reference +description: Access device and system functionality in your Expo and React Native apps using Expo SDK packages. +hideTOC: true +--- + +import { BookOpen02Icon } from '@expo/styleguide-icons/outline/BookOpen02Icon'; + +import { BoxLink } from '~/ui/components/BoxLink'; +import { Collapsible } from '~/ui/components/Collapsible'; +import RedirectNotification from '~/ui/components/RedirectNotification'; +import { + AndroidIOSCompatibilityTable, + ReactNativeCompatibilityTable, +} from '~/ui/components/SDKTables'; +import { Terminal } from '~/ui/components/Snippet'; +import { CODE } from '~/ui/components/Text'; + + + The page you are looking for does not exist in this SDK version. It may have been deprecated or + added to a newer SDK version. + + +The Expo SDK is a collection of packages that provide access to device and system functionality such as camera, contacts, location, sensors, haptics, and more. Each package targets a specific feature and can be used independently. All packages work in any React Native app with the `expo` package installed. + +You can install any Expo SDK package using the [`npx expo install`](/more/expo-cli/#install) command. For example, three different packages are installed using the following command: + + + +After installing one or more packages, you can import them into your JavaScript code: + +```ts +import { CameraView } from 'expo-camera'; +import * as Contacts from 'expo-contacts'; +import { Gyroscope } from 'expo-sensors'; +``` + +This allows you to write [`Contacts.getContactsAsync()`](./sdk/contacts#contactsgetcontactsasynccontactquery) and read the contacts from the device, read the gyroscope sensor to detect device movement, or start the phone's camera and take photos. + +## All Expo SDK packages work in any React Native app + +Expo apps are React Native apps, so all Expo SDK packages work in any React Native app with the `expo` package installed and configured. The easiest way to create a React Native app with support for Expo SDK packages is to use `create-expo-app`. However, you can also add Expo SDK support to an existing React Native app with the `npx install-expo-modules` command. + + + + + Learn more about configuring projects created with{' '} + npx @react-native-community/cli@latest init to Expo SDK packages. + + } + Icon={BookOpen02Icon} +/> + + + +## Using pre-release versions + +New Expo SDK versions are released three times each year. Between these releases, we publish pre-release versions of the `expo` package and all of the Expo SDK packages. Pre-releases are not considered stable and should only be used if you are comfortable with the risk of encountering bugs or other issues. + +### Canary releases + +Canary releases represent a snapshot of the state of the `main` branch at the time they are published. Canary package versions include `-canary` in the name, along with the date and commit hash, such as `55.0.0-canary-20260121-a63c0dd`. To install the latest canary release: + + + +You can often use pre-release versions of individual packages with stable releases of the Expo SDK. There may occasionally be incompatibilities or other issues that arise in canary-quality releases. You may want to [silence dependency validation warnings](/more/expo-cli/#configuring-dependency-validation) if you opt in to the canary package and once you have verified that it works well for your use cases. + +### Beta releases + +Before each Expo SDK release, we publish beta versions of the `expo` package and all of the Expo SDK packages. Beta releases are considered much more stable than canary releases, and we encourage developers to try them out on their apps and share their feedback. Beta releases use the `beta` tag on npm and follow the instructions in the related [changelog](https://expo.dev/changelog) post. + +## Each Expo SDK version depends on a React Native version + + + +### Additional information + + + +- Expo SDK versions are released three times each year, and each Expo SDK release targets a single React Native version. This is typically the latest stable version at the time of the release. +- The release cadence of React Native has varied over its history and it is currently on pace for six releases in 2025. While on this cadence, you can expect that there will be an Expo SDK version for every second React Native release. +- Pre-release versions of the upcoming Expo SDK will include support for the latest version of React Native quickly, usually the same day it is released. A member of the Expo SDK team works on the React Native releases team for each release, and is responsible for continuously updating the React Native version in the Expo repository, verifying compatibility, and reporting regressions back to the team at Meta. + + + + + +At Expo, we have found that releasing three major version provides a good balance of stability and innovation for developers depending on our open source tools. Expo and Meta work closely together on releases, and we will keep improving our processes to get the latest Expo and React Native features to you as quickly as possible. + + + + +We work closely with the team at Meta to ensure that any urgent fixes are included in the React Native version used by the latest Expo SDK. If your issue won't be cherrypicked into an existing release because it is more niche, or it involves a breaking change, then you have two options: + +1. Use [`patch-package`](https://github.com/ds300/patch-package) to pull in the fix. +2. Use a [pre-release version of the Expo SDK](#using-pre-release-versions). An ([example](https://expo.dev/changelog/react-native-78)). + + + + + +Packages in the Expo SDK are intended to support the target React Native version for that SDK. Typically, they will not support older versions of React Native, but they may. When a new version of React Native is released, the latest versions of the Expo SDK packages are typically updated to support it. However, this may take weeks or more, depending on the extent of the changes in the release. + + + +## Support for Android and iOS versions + +Each version of Expo SDK supports a minimum OS version of Android and iOS. For Android, the `compileSdkVersion` is defined which tells the [Gradle](https://developer.android.com/studio/build) which Android SDK version to use to compile the app. This also means that you can use the Android API features included in that SDK version and from the previous versions. For iOS, the [Xcode](https://developer.apple.com/news/upcoming-requirements/) tells the minimum Xcode SDK version to use to compile the app. + + + +When deciding whether to upgrade your Expo SDK version, consider both Expo's SDK version and app store submission requirements, as described in the above table. Google Play Store and Apple App Store periodically increase their minimum required OS versions and API levels, which are required for new app submissions. Expo has no control over the app store requirements, and you should check [Google](https://developer.android.com/studio/build) and [Apple](https://developer.apple.com/news/upcoming-requirements/) for the current store submission requirements. diff --git a/docs/pages/versions/v56.0.0/sdk/accelerometer.mdx b/docs/pages/versions/v56.0.0/sdk/accelerometer.mdx new file mode 100644 index 00000000000000..ed39f2b8aa6108 --- /dev/null +++ b/docs/pages/versions/v56.0.0/sdk/accelerometer.mdx @@ -0,0 +1,112 @@ +--- +title: Accelerometer +description: A library that provides access to the device's accelerometer sensor. +sourceCodeUrl: 'https://github.com/expo/expo/tree/sdk-56/packages/expo-sensors' +packageName: 'expo-sensors' +iconUrl: '/static/images/packages/expo-sensors.png' +platforms: ['android', 'ios*', 'web', 'expo-go'] +--- + +import APISection from '~/components/plugins/APISection'; +import { APIInstallSection } from '~/components/plugins/InstallSection'; +import { SnackInline } from '~/ui/components/Snippet'; + +`Accelerometer` from `expo-sensors` provides access to the device accelerometer sensor(s) and associated listeners to respond to changes in acceleration in three-dimensional space, meaning any movement or vibration. + +## Installation + + + +## Usage + + + +```jsx +import { useState, useEffect } from 'react'; +import { StyleSheet, Text, TouchableOpacity, View } from 'react-native'; +import { Accelerometer } from 'expo-sensors'; + +export default function App() { + const [{ x, y, z }, setData] = useState({ + x: 0, + y: 0, + z: 0, + }); + const [subscription, setSubscription] = useState(null); + + const _slow = () => Accelerometer.setUpdateInterval(1000); + const _fast = () => Accelerometer.setUpdateInterval(16); + + const _subscribe = () => { + setSubscription(Accelerometer.addListener(setData)); + }; + + const _unsubscribe = () => { + subscription && subscription.remove(); + setSubscription(null); + }; + + useEffect(() => { + _subscribe(); + return () => _unsubscribe(); + }, []); + + return ( + + Accelerometer: (in gs where 1g = 9.81 m/s^2) + x: {x} + y: {y} + z: {z} + + + {subscription ? 'On' : 'Off'} + + + Slow + + + Fast + + + + ); +} + +const styles = StyleSheet.create({ + container: { + flex: 1, + justifyContent: 'center', + paddingHorizontal: 20, + }, + text: { + textAlign: 'center', + }, + buttonContainer: { + flexDirection: 'row', + alignItems: 'stretch', + marginTop: 15, + }, + button: { + flex: 1, + justifyContent: 'center', + alignItems: 'center', + backgroundColor: '#eee', + padding: 10, + }, + middleButton: { + borderLeftWidth: 1, + borderRightWidth: 1, + borderColor: '#ccc', + }, +}); +``` + + + +## API + +```js +import { Accelerometer } from 'expo-sensors'; +``` + + diff --git a/docs/pages/versions/v56.0.0/sdk/age-range.mdx b/docs/pages/versions/v56.0.0/sdk/age-range.mdx new file mode 100644 index 00000000000000..de40df85b6ad3c --- /dev/null +++ b/docs/pages/versions/v56.0.0/sdk/age-range.mdx @@ -0,0 +1,132 @@ +--- +title: AgeRange +description: A library that provides access to age range information using Play Age Signals API on Android and Declared Age Range framework on iOS. +sourceCodeUrl: 'https://github.com/expo/expo/tree/sdk-56/packages/expo-age-range' +packageName: 'expo-age-range' +platforms: ['android', 'ios', 'expo-go'] +isAlpha: true +--- + +import APISection from '~/components/plugins/APISection'; +import { APIInstallSection } from '~/components/plugins/InstallSection'; +import { ConfigReactNative } from '~/ui/components/ConfigSection'; +import { SnackInline } from '~/ui/components/Snippet'; +import { PlatformTags } from '~/ui/components/Tag/PlatformTags'; + +> **important** **This library is currently in [alpha](/more/release-statuses/#alpha) and will frequently experience breaking changes.** + +`expo-age-range` provides access to user age range information. It uses Google's [Play Age Signals API](https://developer.android.com/google/play/age-signals/use-age-signals-api) on Android and Apple's [Declared Age Range framework](https://developer.apple.com/documentation/declaredagerange/) on iOS. + +This library allows you to request age range information from your app users to help you comply with age-appropriate content regulations (such as in [Texas, USA](https://developer.apple.com/news/?id=btkirlj8)) and provide age-appropriate experiences in your app. + +### Limitations + +We strongly recommend testing the functionality on a real device, as simulator runtimes may not work as expected. + +## Installation + + + +## Configuration in app config + +### Setup iOS project + +To use the age range API on iOS, you need to build your project with Xcode 26.0 or later. The `com.apple.developer.declared-age-range` entitlement is required. Add it to your [app config](../config/app/) file: + +```json app.json +{ + "expo": { + "ios": { + "entitlements": { + "com.apple.developer.declared-age-range": true + } + } + } +} +``` + + + +For existing React Native projects, add the entitlement to your project's **ios/[app]/[app].entitlements** file: + +```xml +com.apple.developer.declared-age-range + +``` + + + +## Usage + + + +```tsx +import * as AgeRange from 'expo-age-range'; +import { useState } from 'react'; +import { StyleSheet, Text, View, Button } from 'react-native'; + +export default function App() { + const [result, setResult] = useState(null); + + const requestAgeRange = async () => { + try { + const ageRange = await AgeRange.requestAgeRangeAsync({ + threshold1: 10, + threshold2: 13, + threshold3: 18, + }); + setResult(ageRange); + } catch (error) { + setResult({ error: error.message }); + } + }; + + return ( + + + {visible && ( + setVisible(false)}> + + Confirm Action + + + Are you sure you want to proceed? + + + setVisible(false)}> + Confirm + + + + setVisible(false)}> + Cancel + + + + )} + + ); +} +``` + +### Custom colors + +```tsx CustomColorsExample.tsx +import { useState } from 'react'; +import { Host, AlertDialog, Button, TextButton, Text } from '@expo/ui/jetpack-compose'; + +export default function CustomColorsExample() { + const [visible, setVisible] = useState(false); + + return ( + + + {visible && ( + setVisible(false)} + colors={{ + containerColor: '#1E1E2E', + titleContentColor: '#CDD6F4', + textContentColor: '#BAC2DE', + }}> + + Custom Dialog + + + This dialog uses custom colors. + + + setVisible(false)}> + OK + + + + setVisible(false)}> + Cancel + + + + )} + + ); +} +``` + +### With icon + +```tsx IconDialogExample.tsx +import { useState } from 'react'; +import { Host, AlertDialog, Button, TextButton, Text, Icon } from '@expo/ui/jetpack-compose'; + +export default function IconDialogExample() { + const [visible, setVisible] = useState(false); + + return ( + + + {visible && ( + setVisible(false)}> + + {/* Replace with your own icon asset */} + + + + Dialog with Icon + + + This dialog has an icon above the title. + + + setVisible(false)}> + OK + + + + )} + + ); +} +``` + +## API + +```tsx +import { AlertDialog } from '@expo/ui/jetpack-compose'; +``` + + diff --git a/docs/pages/versions/v56.0.0/sdk/ui/jetpack-compose/badge.mdx b/docs/pages/versions/v56.0.0/sdk/ui/jetpack-compose/badge.mdx new file mode 100644 index 00000000000000..3724bb9352e8ed --- /dev/null +++ b/docs/pages/versions/v56.0.0/sdk/ui/jetpack-compose/badge.mdx @@ -0,0 +1,60 @@ +--- +title: Badge +description: A Jetpack Compose Badge component for displaying status indicators and counts. +sourceCodeUrl: 'https://github.com/expo/expo/tree/sdk-56/packages/expo-ui' +packageName: '@expo/ui' +platforms: ['android', 'expo-go'] +--- + +import APISection from '~/components/plugins/APISection'; +import { APIInstallSection } from '~/components/plugins/InstallSection'; + +Expo UI Badge matches the official Jetpack Compose [`Badge`](https://developer.android.com/develop/ui/compose/components/badges) API. It renders as a small colored indicator dot, or with content such as a count number. + +## Installation + + + +## Usage + +### Indicator dot + +A badge with no children renders as a small dot indicator. + +```tsx BadgeDot.tsx +import { Host, Badge } from '@expo/ui/jetpack-compose'; + +export default function BadgeDot() { + return ( + + + + ); +} +``` + +### Badge with count + +Pass a `Text` child to display a number or label. + +```tsx BadgeCount.tsx +import { Host, Badge, Text } from '@expo/ui/jetpack-compose'; + +export default function BadgeCount() { + return ( + + + 3 + + + ); +} +``` + +## API + +```tsx +import { Badge } from '@expo/ui/jetpack-compose'; +``` + + diff --git a/docs/pages/versions/v56.0.0/sdk/ui/jetpack-compose/badgedbox.mdx b/docs/pages/versions/v56.0.0/sdk/ui/jetpack-compose/badgedbox.mdx new file mode 100644 index 00000000000000..ec92bdcea8de1c --- /dev/null +++ b/docs/pages/versions/v56.0.0/sdk/ui/jetpack-compose/badgedbox.mdx @@ -0,0 +1,84 @@ +--- +title: BadgedBox +description: A Jetpack Compose BadgedBox component for overlaying badges on content. +sourceCodeUrl: 'https://github.com/expo/expo/tree/sdk-56/packages/expo-ui' +packageName: '@expo/ui' +platforms: ['android', 'expo-go'] +--- + +import APISection from '~/components/plugins/APISection'; +import { APIInstallSection } from '~/components/plugins/InstallSection'; + +Expo UI BadgedBox matches the official Jetpack Compose [`BadgedBox`](https://developer.android.com/develop/ui/compose/components/badges) API. It overlays a badge on top of content such as an icon. + +## Installation + + + +## Usage + +### Icon with badge count + +```tsx IconWithBadge.tsx +import { Host, Badge, BadgedBox, Icon, Text } from '@expo/ui/jetpack-compose'; + +// Replace with your own vector drawable asset +const mailIcon = require('./assets/mail.xml'); + +export default function IconWithBadge() { + return ( + + + + + 5 + + + + + + ); +} +``` + +### Interactive counter + +```tsx InteractiveBadge.tsx +import { useState } from 'react'; +import { Host, Badge, BadgedBox, Icon, Button, Text, Column } from '@expo/ui/jetpack-compose'; + +// Replace with your own vector drawable asset +const cartIcon = require('./assets/cart.xml'); + +export default function InteractiveBadge() { + const [count, setCount] = useState(0); + + return ( + + + + + {count > 0 ? ( + + {String(count)} + + ) : null} + + + + + + + ); +} +``` + +## API + +```tsx +import { BadgedBox } from '@expo/ui/jetpack-compose'; +``` + + diff --git a/docs/pages/versions/v56.0.0/sdk/ui/jetpack-compose/basicalertdialog.mdx b/docs/pages/versions/v56.0.0/sdk/ui/jetpack-compose/basicalertdialog.mdx new file mode 100644 index 00000000000000..55c8a24721deec --- /dev/null +++ b/docs/pages/versions/v56.0.0/sdk/ui/jetpack-compose/basicalertdialog.mdx @@ -0,0 +1,81 @@ +--- +title: BasicAlertDialog +description: A Jetpack Compose BasicAlertDialog component for displaying dialogs with custom content. +sourceCodeUrl: 'https://github.com/expo/expo/tree/sdk-56/packages/expo-ui' +packageName: '@expo/ui' +platforms: ['android', 'expo-go'] +--- + +import APISection from '~/components/plugins/APISection'; +import { APIInstallSection } from '~/components/plugins/InstallSection'; + +Expo UI BasicAlertDialog matches the official Jetpack Compose [BasicAlertDialog](https://developer.android.com/develop/ui/compose/components/dialog) API and displays a minimal dialog that accepts custom children as its content, giving you full control over the dialog layout. + +## Installation + + + +## Usage + +### Basic dialog with custom content + +```tsx BasicAlertDialogExample.tsx +import { useState } from 'react'; +import { + Host, + BasicAlertDialog, + Button, + TextButton, + Text, + Surface, + Column, + Spacer, +} from '@expo/ui/jetpack-compose'; +import { + padding, + wrapContentWidth, + wrapContentHeight, + clip, + height, + align, + Shapes, +} from '@expo/ui/jetpack-compose/modifiers'; + +export default function BasicAlertDialogExample() { + const [visible, setVisible] = useState(false); + + return ( + + + {visible && ( + setVisible(false)}> + + + + This area typically contains the supportive text which presents the details + regarding the Dialog's purpose. + + + setVisible(false)} modifiers={[align('centerEnd')]}> + Confirm + + + + + )} + + ); +} +``` + +## API + +```tsx +import { BasicAlertDialog } from '@expo/ui/jetpack-compose'; +``` + + diff --git a/docs/pages/versions/v56.0.0/sdk/ui/jetpack-compose/bottomsheet.mdx b/docs/pages/versions/v56.0.0/sdk/ui/jetpack-compose/bottomsheet.mdx new file mode 100644 index 00000000000000..367780c38ceaab --- /dev/null +++ b/docs/pages/versions/v56.0.0/sdk/ui/jetpack-compose/bottomsheet.mdx @@ -0,0 +1,362 @@ +--- +title: ModalBottomSheet +description: A Jetpack Compose ModalBottomSheet component that presents content from the bottom of the screen. +sourceCodeUrl: 'https://github.com/expo/expo/tree/sdk-56/packages/expo-ui' +packageName: '@expo/ui' +platforms: ['android', 'expo-go'] +--- + +import APISection from '~/components/plugins/APISection'; +import { APIInstallSection } from '~/components/plugins/InstallSection'; + +Expo UI ModalBottomSheet matches the official Jetpack Compose [Bottom Sheet API](https://developer.android.com/develop/ui/compose/components/bottom-sheets) and displays content in a modal sheet that slides up from the bottom. + +## Installation + + + +## Usage + +### Basic bottom sheet + +Use `ref.hide()` to programmatically dismiss the sheet with an animation before unmounting it. + +```tsx BasicBottomSheetExample.tsx +import { useRef, useState } from 'react'; +import { Host, ModalBottomSheet, Button, Column, Text } from '@expo/ui/jetpack-compose'; +import type { ModalBottomSheetRef } from '@expo/ui/jetpack-compose'; +import { paddingAll } from '@expo/ui/jetpack-compose/modifiers'; + +export default function BasicBottomSheetExample() { + const [visible, setVisible] = useState(false); + const sheetRef = useRef(null); + + const hideSheet = async () => { + await sheetRef.current?.hide(); + setVisible(false); + }; + + return ( + + + {visible && ( + setVisible(false)}> + + Hello from bottom sheet! + You can add more content here. + + + + )} + + ); +} +``` + +### Skip partially expanded state + +When `skipPartiallyExpanded` is set, the sheet opens directly in the fully expanded state instead of stopping at the half-height position first. + +```tsx SkipPartiallyExpandedExample.tsx +import { useRef, useState } from 'react'; +import { Host, ModalBottomSheet, Button, Column, Text } from '@expo/ui/jetpack-compose'; +import type { ModalBottomSheetRef } from '@expo/ui/jetpack-compose'; +import { paddingAll } from '@expo/ui/jetpack-compose/modifiers'; + +export default function SkipPartiallyExpandedExample() { + const [visible, setVisible] = useState(false); + const sheetRef = useRef(null); + + const hideSheet = async () => { + await sheetRef.current?.hide(); + setVisible(false); + }; + + return ( + + + {visible && ( + setVisible(false)} + skipPartiallyExpanded> + + This sheet skips the partially expanded state. + It opens directly in the fully expanded position. + + + + )} + + ); +} +``` + +### Custom colors + +Use `containerColor`, `contentColor`, and `scrimColor` to customize the sheet's appearance. + +```tsx CustomColorsExample.tsx +import { useRef, useState } from 'react'; +import { Host, ModalBottomSheet, Button, Column, Text } from '@expo/ui/jetpack-compose'; +import type { ModalBottomSheetRef } from '@expo/ui/jetpack-compose'; +import { paddingAll } from '@expo/ui/jetpack-compose/modifiers'; + +export default function CustomColorsExample() { + const [visible, setVisible] = useState(false); + const sheetRef = useRef(null); + + const hideSheet = async () => { + await sheetRef.current?.hide(); + setVisible(false); + }; + + return ( + + + {visible && ( + setVisible(false)} + containerColor="#1a1a2e" + contentColor="#e0e0e0" + scrimColor="#806200EE"> + + Custom styled bottom sheet. + Dark container with a purple scrim overlay. + + + + )} + + ); +} +``` + +### Custom drag handle + +Use `ModalBottomSheet.DragHandle` slot to provide a custom drag handle, or set `showDragHandle={false}` to hide it entirely. + +```tsx CustomDragHandleExample.tsx +import { useRef, useState } from 'react'; +import { Host, ModalBottomSheet, Button, Column, Box, Text } from '@expo/ui/jetpack-compose'; +import type { ModalBottomSheetRef } from '@expo/ui/jetpack-compose'; +import { + background, + clip, + fillMaxWidth, + height, + padding, + Shapes, + width, +} from '@expo/ui/jetpack-compose/modifiers'; + +export default function CustomDragHandleExample() { + const [visible, setVisible] = useState(false); + const sheetRef = useRef(null); + + const hideSheet = async () => { + await sheetRef.current?.hide(); + setVisible(false); + }; + + return ( + + + {visible && ( + setVisible(false)}> + + + + + + + + + + )} + + ); +} +``` + +### React Native content inside a bottom sheet + +Use `RNHostView` to embed interactive React Native views inside a Compose bottom sheet. This lets you mix Compose layout with RN components like `Pressable` and `Text`. + +```tsx RNContentBottomSheetExample.tsx +import { useRef, useState } from 'react'; +import { Host, ModalBottomSheet, Button, Column, RNHostView, Text } from '@expo/ui/jetpack-compose'; +import type { ModalBottomSheetRef } from '@expo/ui/jetpack-compose'; +import { padding } from '@expo/ui/jetpack-compose/modifiers'; +import { Pressable, Text as RNText, View } from 'react-native'; + +export default function RNContentBottomSheetExample() { + const [visible, setVisible] = useState(false); + const sheetRef = useRef(null); + + const hideSheet = async () => { + await sheetRef.current?.hide(); + setVisible(false); + }; + + return ( + + + {visible && ( + setVisible(false)} + skipPartiallyExpanded={false}> + + Mixing Compose + RN in a Bottom Sheet + + + + React Native Content + + + Close + + + + + + )} + + ); +} +``` + +### React Native content with flex + +Use `RNHostView` without `matchContents` to let the RN view fill the remaining space inside the sheet. Combine with a fixed `height` modifier on the parent `Column` to control the sheet size. + +```tsx FlexRNContentExample.tsx +import { useRef, useState } from 'react'; +import { Host, ModalBottomSheet, Button, Column, RNHostView, Text } from '@expo/ui/jetpack-compose'; +import type { ModalBottomSheetRef } from '@expo/ui/jetpack-compose'; +import { height, padding } from '@expo/ui/jetpack-compose/modifiers'; +import { Text as RNText, View } from 'react-native'; + +export default function FlexRNContentExample() { + const [visible, setVisible] = useState(false); + const sheetRef = useRef(null); + + const hideSheet = async () => { + await sheetRef.current?.hide(); + setVisible(false); + }; + + return ( + + + {visible && ( + setVisible(false)} + skipPartiallyExpanded> + + RN View with flex: 1 + + + + React Native Content (flex: 1) + + + + + + )} + + ); +} +``` + +### Non-dismissible sheet + +Combine `properties`, `sheetGesturesEnabled` to create a sheet that can only be closed programmatically. + +```tsx NonDismissibleExample.tsx +import { useRef, useState } from 'react'; +import { Host, ModalBottomSheet, Button, Column, Text } from '@expo/ui/jetpack-compose'; +import type { ModalBottomSheetRef } from '@expo/ui/jetpack-compose'; +import { paddingAll } from '@expo/ui/jetpack-compose/modifiers'; + +export default function NonDismissibleExample() { + const [visible, setVisible] = useState(false); + const sheetRef = useRef(null); + + const hideSheet = async () => { + await sheetRef.current?.hide(); + setVisible(false); + }; + + return ( + + + {visible && ( + setVisible(false)} + sheetGesturesEnabled={false} + properties={{ + shouldDismissOnBackPress: false, + shouldDismissOnClickOutside: false, + }}> + + This sheet cannot be dismissed by swiping, back press, or tapping outside. + Only the button below will close it. + + + + )} + + ); +} +``` + +## API + +```tsx +import { ModalBottomSheet } from '@expo/ui/jetpack-compose'; +``` + + diff --git a/docs/pages/versions/v56.0.0/sdk/ui/jetpack-compose/box.mdx b/docs/pages/versions/v56.0.0/sdk/ui/jetpack-compose/box.mdx new file mode 100644 index 00000000000000..23275bc5599c6a --- /dev/null +++ b/docs/pages/versions/v56.0.0/sdk/ui/jetpack-compose/box.mdx @@ -0,0 +1,43 @@ +--- +title: Box +description: A Jetpack Compose Box component for stacking child elements. +sourceCodeUrl: 'https://github.com/expo/expo/tree/sdk-56/packages/expo-ui' +packageName: '@expo/ui' +platforms: ['android', 'expo-go'] +--- + +import APISection from '~/components/plugins/APISection'; +import { APIInstallSection } from '~/components/plugins/InstallSection'; + +Expo UI Box matches the official Jetpack Compose [Box](https://developer.android.com/reference/kotlin/androidx/compose/foundation/layout/package-summary#Box) API and stacks children on top of each other with configurable content alignment. + +## Installation + + + +## Usage + +`Box` stacks children on top of each other. Use `contentAlignment` to position them within the box. + +```tsx BoxExample.tsx +import { Host, Box, Text } from '@expo/ui/jetpack-compose'; +import { size, background } from '@expo/ui/jetpack-compose/modifiers'; + +export default function BoxExample() { + return ( + + + Centered in Box + + + ); +} +``` + +## API + +```tsx +import { Box } from '@expo/ui/jetpack-compose'; +``` + + diff --git a/docs/pages/versions/v56.0.0/sdk/ui/jetpack-compose/button.mdx b/docs/pages/versions/v56.0.0/sdk/ui/jetpack-compose/button.mdx new file mode 100644 index 00000000000000..fa9117a41aaddc --- /dev/null +++ b/docs/pages/versions/v56.0.0/sdk/ui/jetpack-compose/button.mdx @@ -0,0 +1,184 @@ +--- +title: Button +description: Jetpack Compose Button components for displaying native Material3 buttons. +sourceCodeUrl: 'https://github.com/expo/expo/tree/sdk-56/packages/expo-ui' +packageName: '@expo/ui' +platforms: ['android', 'expo-go'] +--- + +import APISection from '~/components/plugins/APISection'; +import { APIInstallSection } from '~/components/plugins/InstallSection'; + +Expo UI provides five button components that match the official Jetpack Compose [Button API](https://developer.android.com/develop/ui/compose/components/button): `Button` (filled), `FilledTonalButton`, `OutlinedButton`, `ElevatedButton`, and `TextButton`. All variants share the same props and accept composable children for content. + +| Type | Appearance | Purpose | +| ------------ | --------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| Filled | Solid background with contrasting text. | High-emphasis buttons for primary actions such as "submit" and "save." | +| Filled tonal | Background color varies to match the surface. | Also for primary or significant actions. Filled tonal buttons provide more visual weight and suit functions such as "add to cart" and "Sign in." | +| Elevated | Stands out by having a shadow. | Serves a similar purpose to tonal buttons. Increase elevation to make the button appear even more prominently. | +| Outlined | Features a border with no fill. | Medium-emphasis buttons, containing actions that are important but not primary. They pair well with other buttons to indicate alternative, secondary actions like "Cancel" or "Back." | +| Text | Displays text with no background or border. | Low-emphasis buttons, ideal for less critical actions such as navigational links, or secondary functions like "Learn More" or "View details." | + +## Installation + + + +## Usage + +### Basic button + +A filled button is the default, high-emphasis button for primary actions. + +```tsx BasicButtonExample.tsx +import { Host, Button, Text } from '@expo/ui/jetpack-compose'; + +export default function BasicButtonExample() { + return ( + + + + ); +} +``` + +### Button variants + +Use different button components to convey varying levels of emphasis. + +```tsx ButtonVariantsExample.tsx +import { + Host, + Button, + FilledTonalButton, + OutlinedButton, + ElevatedButton, + TextButton, + Column, + Text, +} from '@expo/ui/jetpack-compose'; + +export default function ButtonVariantsExample() { + return ( + + + + {}}> + Filled Tonal + + {}}> + Outlined + + {}}> + Elevated + + {}}> + Text + + + + ); +} +``` + +### Button with icons + +Since buttons accept composable children, you can add leading and trailing icons using the `Icon` component. This follows the [Material 3 buttons with icon](https://m3.material.io/components/buttons/guidelines) pattern ([official sample](https://cs.android.com/androidx/platform/frameworks/support/+/androidx-main:compose/material3/material3/samples/src/main/java/androidx/compose/material3/samples/ButtonSamples.kt;l=179?q=ButtonWithIconSample)), use 18dp icon size, 8dp spacing between the icon and label. + +```tsx ButtonWithIconsExample.tsx +import { + Host, + Button, + OutlinedButton, + FilledTonalButton, + Icon, + Spacer, + Text, +} from '@expo/ui/jetpack-compose'; +import { width } from '@expo/ui/jetpack-compose/modifiers'; + +const addIcon = require('./assets/add.png'); +const sendIcon = require('./assets/send.png'); + +export default function ButtonWithIconsExample() { + return ( + + {/* Leading icon */} + + + {/* Trailing icon */} + {}}> + Send + + + + + {/* Both leading and trailing icons */} + {}}> + + + Create & Send + + + + + ); +} +``` + +### Custom colors + +Override container and content colors using the `colors` prop. + +```tsx CustomColorsExample.tsx +import { Host, Button, Text } from '@expo/ui/jetpack-compose'; + +export default function CustomColorsExample() { + return ( + + + + ); +} +``` + +### Custom shape + +```tsx CustomShapeExample.tsx +import { Host, Button, Shape, Text } from '@expo/ui/jetpack-compose'; + +export default function CustomShapeExample() { + return ( + + + + ); +} +``` + +## API + +```tsx +import { + Button, + FilledTonalButton, + OutlinedButton, + ElevatedButton, + TextButton, +} from '@expo/ui/jetpack-compose'; +``` + + diff --git a/docs/pages/versions/v56.0.0/sdk/ui/jetpack-compose/card.mdx b/docs/pages/versions/v56.0.0/sdk/ui/jetpack-compose/card.mdx new file mode 100644 index 00000000000000..3459f9ed0d400c --- /dev/null +++ b/docs/pages/versions/v56.0.0/sdk/ui/jetpack-compose/card.mdx @@ -0,0 +1,108 @@ +--- +title: Card +description: A Jetpack Compose Card component for displaying content in a styled container. +sourceCodeUrl: 'https://github.com/expo/expo/tree/sdk-56/packages/expo-ui' +packageName: '@expo/ui' +platforms: ['android', 'expo-go'] +--- + +import APISection from '~/components/plugins/APISection'; +import { APIInstallSection } from '~/components/plugins/InstallSection'; + +Expo UI Card matches the official Jetpack Compose [Card API](https://developer.android.com/develop/ui/compose/components/card) and displays content inside a styled surface container with optional elevation and outline. The `Card` component renders a [filled card](https://developer.android.com/develop/ui/compose/components/card#filled), while `ElevatedCard` and `OutlinedCard` provide raised and bordered variants respectively. + +## Installation + + + +## Usage + +### Basic card + +```tsx BasicCardExample.tsx +import { Host, Card, Text } from '@expo/ui/jetpack-compose'; +import { paddingAll } from '@expo/ui/jetpack-compose/modifiers'; + +export default function BasicCardExample() { + return ( + + + This is a basic card with default styling. + + + ); +} +``` + +### Card types + +Use `Card` (filled), `ElevatedCard`, or `OutlinedCard` for different styles. + +```tsx CardTypesExample.tsx +import { Host, Card, ElevatedCard, OutlinedCard, Text, Column } from '@expo/ui/jetpack-compose'; +import { paddingAll } from '@expo/ui/jetpack-compose/modifiers'; + +export default function CardTypesExample() { + return ( + + + + Filled card + + + Elevated card + + + Outlined card + + + + ); +} +``` + +### Custom elevation + +Use the `elevation` prop (in dp) to control shadow depth. Elevation is most meaningful on `ElevatedCard`, which uses shadow elevation. Filled `Card` uses tonal elevation by default, so changes may be subtle. + +```tsx ElevatedCardExample.tsx +import { Host, ElevatedCard, Text } from '@expo/ui/jetpack-compose'; +import { paddingAll } from '@expo/ui/jetpack-compose/modifiers'; + +export default function ElevatedCardExample() { + return ( + + + Card with 8dp elevation + + + ); +} +``` + +### Custom border + +`Card` and `OutlinedCard` accept a `border` prop to customize stroke width and color. + +```tsx OutlinedCardExample.tsx +import { Host, OutlinedCard, Text } from '@expo/ui/jetpack-compose'; +import { paddingAll } from '@expo/ui/jetpack-compose/modifiers'; + +export default function OutlinedCardExample() { + return ( + + + Card with custom purple border + + + ); +} +``` + +## API + +```tsx +import { Card, ElevatedCard, OutlinedCard } from '@expo/ui/jetpack-compose'; +``` + + diff --git a/docs/pages/versions/v56.0.0/sdk/ui/jetpack-compose/carousel.mdx b/docs/pages/versions/v56.0.0/sdk/ui/jetpack-compose/carousel.mdx new file mode 100644 index 00000000000000..a65ff37e71b875 --- /dev/null +++ b/docs/pages/versions/v56.0.0/sdk/ui/jetpack-compose/carousel.mdx @@ -0,0 +1,122 @@ +--- +title: Carousel +description: Jetpack Compose Carousel components for displaying scrollable collections of items. +sourceCodeUrl: 'https://github.com/expo/expo/tree/sdk-56/packages/expo-ui' +packageName: '@expo/ui' +platforms: ['android', 'expo-go'] +--- + +import APISection from '~/components/plugins/APISection'; +import { APIInstallSection } from '~/components/plugins/InstallSection'; + +Expo UI provides three carousel components matching the official Jetpack Compose [Carousel](https://developer.android.com/develop/ui/compose/components/carousel) API: `HorizontalCenteredHeroCarousel`, `HorizontalMultiBrowseCarousel`, and `HorizontalUncontainedCarousel`. + +> **Note:** Carousel is a horizontally scrollable component, so the parent `Host` must provide a finite width on the scroll axis. Use `matchContents={{ vertical: true }}` together with `style={{ width: '100%' }}` (or any finite width). See [Match contents in Host reference](host/#match-contents) for details. + +## Installation + + + +## Usage + +### HorizontalCenteredHeroCarousel + +Centers one large hero item between two small peek items — ideal for spotlighting content like movie posters. + +```tsx CenteredHeroExample.tsx +import { Host, HorizontalCenteredHeroCarousel, Box, Text } from '@expo/ui/jetpack-compose'; +import { size, background } from '@expo/ui/jetpack-compose/modifiers'; + +export default function CenteredHeroExample() { + const colors = ['#6200EE', '#03DAC5', '#FF5722', '#4CAF50', '#2196F3']; + + return ( + + + {colors.map((color, index) => ( + + Slide {index + 1} + + ))} + + + ); +} +``` + +### HorizontalMultiBrowseCarousel + +Shows a large item alongside smaller peek items, letting users browse what comes next. + +```tsx MultiBrowseExample.tsx +import { Host, HorizontalMultiBrowseCarousel, Box, Text } from '@expo/ui/jetpack-compose'; +import { size, background } from '@expo/ui/jetpack-compose/modifiers'; + +export default function MultiBrowseExample() { + const colors = ['#6200EE', '#03DAC5', '#FF5722', '#4CAF50', '#2196F3']; + + return ( + + + {colors.map((color, index) => ( + + Card {index + 1} + + ))} + + + ); +} +``` + +### HorizontalUncontainedCarousel + +Each item has a fixed width with free-form scrolling. + +```tsx UncontainedExample.tsx +import { Host, HorizontalUncontainedCarousel, Box, Text } from '@expo/ui/jetpack-compose'; +import { size, background } from '@expo/ui/jetpack-compose/modifiers'; + +export default function UncontainedExample() { + const items = ['Photo 1', 'Photo 2', 'Photo 3', 'Photo 4', 'Photo 5']; + + return ( + + + {items.map(item => ( + + {item} + + ))} + + + ); +} +``` + +## API + +```tsx +import { + HorizontalCenteredHeroCarousel, + HorizontalMultiBrowseCarousel, + HorizontalUncontainedCarousel, +} from '@expo/ui/jetpack-compose'; +``` + + diff --git a/docs/pages/versions/v56.0.0/sdk/ui/jetpack-compose/checkbox.mdx b/docs/pages/versions/v56.0.0/sdk/ui/jetpack-compose/checkbox.mdx new file mode 100644 index 00000000000000..34588d32bd5cdc --- /dev/null +++ b/docs/pages/versions/v56.0.0/sdk/ui/jetpack-compose/checkbox.mdx @@ -0,0 +1,130 @@ +--- +title: Checkbox +description: A Jetpack Compose Checkbox component for selection controls. +sourceCodeUrl: 'https://github.com/expo/expo/tree/sdk-56/packages/expo-ui' +packageName: '@expo/ui' +platforms: ['android', 'expo-go'] +--- + +import APISection from '~/components/plugins/APISection'; +import { APIInstallSection } from '~/components/plugins/InstallSection'; + +Expo UI Checkbox matches the official Jetpack Compose [Checkbox](https://developer.android.com/develop/ui/compose/components/checkbox) API. + +## Installation + + + +## Usage + +### Basic checkbox + +```tsx CheckboxExample.tsx +import { useState } from 'react'; +import { Host, Checkbox } from '@expo/ui/jetpack-compose'; + +export default function CheckboxExample() { + const [checked, setChecked] = useState(false); + + return ( + + + + ); +} +``` + +### Custom colors + +```tsx CustomColorsExample.tsx +import { useState } from 'react'; +import { Host, Checkbox } from '@expo/ui/jetpack-compose'; + +export default function CustomColorsExample() { + const [checked, setChecked] = useState(false); + + return ( + + + + ); +} +``` + +### Select all (TriStateCheckbox) + +Use `TriStateCheckbox` for a parent checkbox that reflects the state of its children. It supports three states: `'on'`, `'off'`, and `'indeterminate'`. + +Apply the `toggleable` modifier to each `Row` to make the entire row (checkbox + label) tappable with correct accessibility semantics. When using `toggleable` on the row, omit `onCheckedChange`/`onClick` from the checkbox itself to avoid double-handling. + +```tsx SelectAllExample.tsx +import { useState } from 'react'; +import { Host, Checkbox, TriStateCheckbox, Row, Column, Text } from '@expo/ui/jetpack-compose'; +import { toggleable } from '@expo/ui/jetpack-compose/modifiers'; + +export default function SelectAllExample() { + const [child1, setChild1] = useState(false); + const [child2, setChild2] = useState(false); + const [child3, setChild3] = useState(false); + + const parentState = + child1 && child2 && child3 ? 'on' : !child1 && !child2 && !child3 ? 'off' : 'indeterminate'; + + return ( + + + { + const newState = parentState !== 'on'; + setChild1(newState); + setChild2(newState); + setChild3(newState); + }, + { role: 'checkbox' } + ), + ]}> + + Select all + + setChild1(!child1), { role: 'checkbox' })]}> + + Option 1 + + setChild2(!child2), { role: 'checkbox' })]}> + + Option 2 + + setChild3(!child3), { role: 'checkbox' })]}> + + Option 3 + + + + ); +} +``` + +## API + +```tsx +import { Checkbox, TriStateCheckbox } from '@expo/ui/jetpack-compose'; +``` + + diff --git a/docs/pages/versions/v56.0.0/sdk/ui/jetpack-compose/chip.mdx b/docs/pages/versions/v56.0.0/sdk/ui/jetpack-compose/chip.mdx new file mode 100644 index 00000000000000..9116f19afbce72 --- /dev/null +++ b/docs/pages/versions/v56.0.0/sdk/ui/jetpack-compose/chip.mdx @@ -0,0 +1,125 @@ +--- +title: Chip +description: Jetpack Compose Chip components for displaying compact elements. +sourceCodeUrl: 'https://github.com/expo/expo/tree/sdk-56/packages/expo-ui' +packageName: '@expo/ui' +platforms: ['android', 'expo-go'] +--- + +import APISection from '~/components/plugins/APISection'; +import { APIInstallSection } from '~/components/plugins/InstallSection'; + +Expo UI Chips match the official Jetpack Compose [Chip API](https://developer.android.com/develop/ui/compose/components/chip). Each chip type is a separate component: `AssistChip`, `FilterChip`, `InputChip`, and `SuggestionChip`. + +## Installation + + + +## Usage + +### Assist chip + +Assist chips help users take actions or start tasks, such as booking a flight or opening a map. They often appear as temporary UI elements in response to user input. + +```tsx AssistChipExample.tsx +import { Host, AssistChip, Icon, Text } from '@expo/ui/jetpack-compose'; + +export default function AssistChipExample() { + return ( + + console.log('Opening flight booking...')}> + + Book Flight + + + + + + + ); +} +``` + +### Filter chip + +Filter chips allow users to refine content from a set of options. They support a selected state and are commonly used in search bars or content filtering. + +```tsx FilterChipExample.tsx +import { useState } from 'react'; +import { Host, FilterChip, Text } from '@expo/ui/jetpack-compose'; + +export default function FilterChipExample() { + const [selected, setSelected] = useState(false); + + return ( + + setSelected(!selected)}> + + Images + + + + ); +} +``` + +### Input chip + +Input chips represent discrete pieces of information entered by a user, such as tags in a text field. They support avatars, trailing icons, and can be dismissed. + +```tsx InputChipExample.tsx +import { useState } from 'react'; +import { Host, InputChip, Icon, Text, FlowRow } from '@expo/ui/jetpack-compose'; + +export default function InputChipExample() { + const [chips, setChips] = useState(['Work', 'Travel', 'News']); + + return ( + + + {chips.map(label => ( + setChips(prev => prev.filter(c => c !== label))}> + + {label} + + + + + + ))} + + + ); +} +``` + +### Suggestion chip + +Suggestion chips help narrow a user's intent by presenting dynamically generated suggestions, such as quick-reply options in a chat or search refinements. + +```tsx SuggestionChipExample.tsx +import { Host, SuggestionChip, Text } from '@expo/ui/jetpack-compose'; + +export default function SuggestionChipExample() { + return ( + + console.log('Searching nearby...')}> + + Nearby + + + + ); +} +``` + +## API + +```tsx +import { AssistChip, FilterChip, InputChip, SuggestionChip } from '@expo/ui/jetpack-compose'; +``` + + diff --git a/docs/pages/versions/v56.0.0/sdk/ui/jetpack-compose/colors.mdx b/docs/pages/versions/v56.0.0/sdk/ui/jetpack-compose/colors.mdx new file mode 100644 index 00000000000000..3e7c46a4bafa45 --- /dev/null +++ b/docs/pages/versions/v56.0.0/sdk/ui/jetpack-compose/colors.mdx @@ -0,0 +1,107 @@ +--- +title: Material Colors +description: Read the Material 3 color palette (including Material 3 Dynamic Colors) from JavaScript. +sourceCodeUrl: 'https://github.com/expo/expo/tree/sdk-56/packages/expo-ui' +packageName: '@expo/ui' +platforms: ['android', 'expo-go'] +--- + +import APISection from '~/components/plugins/APISection'; +import { APIInstallSection } from '~/components/plugins/InstallSection'; + +@expo/ui/jetpack-compose exposes the [Material 3 color palette](https://m3.material.io/styles/color/system/overview) used by Jetpack Compose so you can pick a palette source and have every component under a [``](./host) theme from it consistently. + +The palette source depends on the options you pass: + +- **Wallpaper-derived:** Default on Android 12+ when no `seedColor` is given. Uses [Material 3 Dynamic Colors](https://m3.material.io/styles/color/dynamic-color/overview) (Material You). +- **Static [Material 3 baseline](https://m3.material.io/styles/color/roles):** Default fallback on Android 11 and below when no `seedColor` is given. +- **Seeded from a color:** When you pass a `seedColor`, the full palette is derived using the same algorithm that Material 3 Dynamic Colors use for wallpaper-based colors. Works on every Android API level and is independent of the wallpaper. + +## Installation + + + +## Usage + +### Theming `` from a seed color + +[`Host`](./host) accepts `seedColor` and `colorScheme` props directly. This is the recommended way to theme a Compose subtree. Native Compose components under this `Host` render with the seeded palette and any descendant that calls [`useMaterialColors()`](#usematerialcolorsoptions) without arguments receives the same palette from the Host's context. + +```tsx BrandedHostExample.tsx +import { Button, Host, Text } from '@expo/ui/jetpack-compose'; + +export default function BrandedHostExample() { + return ( + + + + ); +} +``` + +### Reading the current palette inside Host + +Call [`useMaterialColors()`](#usematerialcolorsoptions) without arguments inside a [``](./host) to read the Host's current palette. The hook returns a reference-stable [`MaterialColors`](#materialcolors) object and does not cross the native bridge on re-renders. + +```tsx MaterialColorsExample.tsx +import { Column, Host, Text, useMaterialColors } from '@expo/ui/jetpack-compose'; +import { padding } from '@expo/ui/jetpack-compose/modifiers'; + +export default function MaterialColorsExample() { + return ( + + + + ); +} + +function PaletteInspector() { + const colors = useMaterialColors(); + return ( + + Primary: {colors.primary} + Surface: {colors.surface} + + ); +} +``` + +### Computing a specific palette with arguments + +Pass arguments to [`useMaterialColors()`](#usematerialcolorsoptions) to compute a palette on demand, even outside a ``. The `scheme` takes `'light'` or `'dark'`, you can omit it to follow the system. + +```tsx UseMaterialColorsExample.tsx +import { Column, Host, Text, useMaterialColors } from '@expo/ui/jetpack-compose'; +import { padding } from '@expo/ui/jetpack-compose/modifiers'; + +export default function UseMaterialColorsExample() { + const dark = useMaterialColors({ scheme: 'dark' }); + const brand = useMaterialColors({ seedColor: '#8E24AA' }); + const brandedDark = useMaterialColors({ scheme: 'dark', seedColor: '#8E24AA' }); + + return ( + + + Dark primary: {dark.primary} + Brand primary: {brand.primary} + Branded dark primary: {brandedDark.primary} + + + ); +} +``` + +### Reading colors outside React components + +```tsx GetMaterialColorsExample.tsx +import { getMaterialColors, isDynamicColorAvailable } from '@expo/ui/jetpack-compose'; + +const palette = getMaterialColors({ seedColor: '#8E24AA' }); +console.log('available:', isDynamicColorAvailable, 'primary:', palette.primary); +``` + +## API + + diff --git a/docs/pages/versions/v56.0.0/sdk/ui/jetpack-compose/column.mdx b/docs/pages/versions/v56.0.0/sdk/ui/jetpack-compose/column.mdx new file mode 100644 index 00000000000000..5dda7df9769d3c --- /dev/null +++ b/docs/pages/versions/v56.0.0/sdk/ui/jetpack-compose/column.mdx @@ -0,0 +1,48 @@ +--- +title: Column +description: A Jetpack Compose Column component for placing children vertically. +sourceCodeUrl: 'https://github.com/expo/expo/tree/sdk-56/packages/expo-ui' +packageName: '@expo/ui' +platforms: ['android', 'expo-go'] +--- + +import APISection from '~/components/plugins/APISection'; +import { APIInstallSection } from '~/components/plugins/InstallSection'; + +Expo UI Column matches the official Jetpack Compose [Column](https://developer.android.com/reference/kotlin/androidx/compose/foundation/layout/package-summary#Column) API and places children vertically with configurable arrangement and alignment. + +## Installation + + + +## Usage + +`Column` places children vertically. Use `verticalArrangement` and `horizontalAlignment` to control spacing and alignment. + +```tsx ColumnExample.tsx +import { Host, Column, Text } from '@expo/ui/jetpack-compose'; +import { fillMaxWidth, paddingAll } from '@expo/ui/jetpack-compose/modifiers'; + +export default function ColumnExample() { + return ( + + + First + Second + Third + + + ); +} +``` + +## API + +```tsx +import { Column } from '@expo/ui/jetpack-compose'; +``` + + diff --git a/docs/pages/versions/v56.0.0/sdk/ui/jetpack-compose/datetimepicker.mdx b/docs/pages/versions/v56.0.0/sdk/ui/jetpack-compose/datetimepicker.mdx new file mode 100644 index 00000000000000..4443129e9792d7 --- /dev/null +++ b/docs/pages/versions/v56.0.0/sdk/ui/jetpack-compose/datetimepicker.mdx @@ -0,0 +1,102 @@ +--- +title: DateTimePicker +description: A Jetpack Compose DateTimePicker component for selecting dates and times. +sourceCodeUrl: 'https://github.com/expo/expo/tree/sdk-56/packages/expo-ui' +packageName: '@expo/ui' +platforms: ['android', 'expo-go'] +--- + +import APISection from '~/components/plugins/APISection'; +import { APIInstallSection } from '~/components/plugins/InstallSection'; + +Expo UI DateTimePicker matches the official Jetpack Compose [Date Picker](https://developer.android.com/develop/ui/compose/components/datepickers) and [Time Picker](https://developer.android.com/develop/ui/compose/components/time-pickers) APIs and supports date, time, and combined selection. + +> **Note:** The date variants render Material's calendar grid and input field, both of which scroll horizontally internally. The parent `Host` must provide a finite width on the horizontal axis, use `matchContents={{ vertical: true }}` together with `style={{ width: '100%' }}` (or any finite width). See [Match contents in Host reference](host/#match-contents) for details. + +## Installation + + + +## Usage + +### Date picker + +```tsx DatePickerExample.tsx +import { useState } from 'react'; +import { Host, DateTimePicker } from '@expo/ui/jetpack-compose'; + +export default function DatePickerExample() { + const [selectedDate, setSelectedDate] = useState(new Date()); + + return ( + + { + setSelectedDate(date); + }} + displayedComponents="date" + initialDate={selectedDate.toISOString()} + variant="picker" + /> + + ); +} +``` + +### Time picker + +```tsx TimePickerExample.tsx +import { useState } from 'react'; +import { Host, DateTimePicker } from '@expo/ui/jetpack-compose'; + +export default function TimePickerExample() { + const [selectedDate, setSelectedDate] = useState(new Date()); + + return ( + + { + setSelectedDate(date); + }} + displayedComponents="hourAndMinute" + initialDate={selectedDate.toISOString()} + variant="picker" + /> + + ); +} +``` + +### Input variant + +Use `variant="input"` to display the picker as a text input field instead of the default picker UI. + +```tsx InputVariantExample.tsx +import { useState } from 'react'; +import { Host, DateTimePicker } from '@expo/ui/jetpack-compose'; + +export default function InputVariantExample() { + const [selectedDate, setSelectedDate] = useState(new Date()); + + return ( + + { + setSelectedDate(date); + }} + displayedComponents="date" + initialDate={selectedDate.toISOString()} + variant="input" + /> + + ); +} +``` + +## API + +```tsx +import { DateTimePicker } from '@expo/ui/jetpack-compose'; +``` + + diff --git a/docs/pages/versions/v56.0.0/sdk/ui/jetpack-compose/divider.mdx b/docs/pages/versions/v56.0.0/sdk/ui/jetpack-compose/divider.mdx new file mode 100644 index 00000000000000..5d8b0fe4eaa7f2 --- /dev/null +++ b/docs/pages/versions/v56.0.0/sdk/ui/jetpack-compose/divider.mdx @@ -0,0 +1,90 @@ +--- +title: Divider +description: Jetpack Compose Divider components for creating visual separators. +sourceCodeUrl: 'https://github.com/expo/expo/tree/sdk-56/packages/expo-ui' +packageName: '@expo/ui' +platforms: ['android', 'expo-go'] +--- + +import APISection from '~/components/plugins/APISection'; +import { APIInstallSection } from '~/components/plugins/InstallSection'; + +Expo UI provides [`HorizontalDivider`]() and [`VerticalDivider`]() matching the official Jetpack Compose Divider API. + +## Installation + + + +## Usage + +### Horizontal divider + +A thin horizontal line to visually separate content in lists and layouts. + +```tsx HorizontalDividerExample.tsx +import { Host, HorizontalDivider, Column, Text } from '@expo/ui/jetpack-compose'; + +export default function HorizontalDividerExample() { + return ( + + + First section + + Second section + + + ); +} +``` + +### Custom thickness and color + +Both `HorizontalDivider` and `VerticalDivider` accept `thickness` and `color` props. Use `StyleSheet.hairlineWidth` for a single-pixel line, or set a custom thickness and color. + +```tsx CustomDividerExample.tsx +import { Host, HorizontalDivider, Column, Text } from '@expo/ui/jetpack-compose'; +import { StyleSheet } from 'react-native'; + +export default function CustomDividerExample() { + return ( + + + Hairline divider (1 pixel) + + Thick colored divider + + Below + + + ); +} +``` + +### Vertical divider + +A vertical line to separate items side by side in a row layout. + +```tsx VerticalDividerExample.tsx +import { Host, VerticalDivider, Row, Text } from '@expo/ui/jetpack-compose'; +import { height } from '@expo/ui/jetpack-compose/modifiers'; + +export default function VerticalDividerExample() { + return ( + + + Left + + Right + + + ); +} +``` + +## API + +```tsx +import { HorizontalDivider, VerticalDivider } from '@expo/ui/jetpack-compose'; +``` + + diff --git a/docs/pages/versions/v56.0.0/sdk/ui/jetpack-compose/dockedsearchbar.mdx b/docs/pages/versions/v56.0.0/sdk/ui/jetpack-compose/dockedsearchbar.mdx new file mode 100644 index 00000000000000..5661f24d03c126 --- /dev/null +++ b/docs/pages/versions/v56.0.0/sdk/ui/jetpack-compose/dockedsearchbar.mdx @@ -0,0 +1,69 @@ +--- +title: DockedSearchBar +description: A Jetpack Compose DockedSearchBar component for displaying an inline search input. +sourceCodeUrl: 'https://github.com/expo/expo/tree/sdk-56/packages/expo-ui' +packageName: '@expo/ui' +platforms: ['android', 'expo-go'] +--- + +import APISection from '~/components/plugins/APISection'; +import { APIInstallSection } from '~/components/plugins/InstallSection'; + +Expo UI DockedSearchBar matches the official Jetpack Compose [SearchBar API](https://developer.android.com/develop/ui/compose/components/search-bar) and displays a search input that remains anchored in its parent layout rather than expanding to full screen. + +## Installation + + + +## Usage + +### Basic docked search bar + +```tsx BasicDockedSearchBarExample.tsx +import { useState } from 'react'; +import { Host, DockedSearchBar } from '@expo/ui/jetpack-compose'; + +export default function BasicDockedSearchBarExample() { + const [query, setQuery] = useState(''); + + return ( + + + + ); +} +``` + +### With placeholder and leading icon + +Use the `DockedSearchBar.Placeholder` and `DockedSearchBar.LeadingIcon` slot components to customize the search bar appearance. + +```tsx DockedSearchBarWithSlotsExample.tsx +import { useState } from 'react'; +import { Host, DockedSearchBar, Text } from '@expo/ui/jetpack-compose'; + +export default function DockedSearchBarWithSlotsExample() { + const [query, setQuery] = useState(''); + + return ( + + + + Search items... + + + 🔍 + + + + ); +} +``` + +## API + +```tsx +import { DockedSearchBar } from '@expo/ui/jetpack-compose'; +``` + + diff --git a/docs/pages/versions/v56.0.0/sdk/ui/jetpack-compose/dropdownmenu.mdx b/docs/pages/versions/v56.0.0/sdk/ui/jetpack-compose/dropdownmenu.mdx new file mode 100644 index 00000000000000..6acbe8b9506892 --- /dev/null +++ b/docs/pages/versions/v56.0.0/sdk/ui/jetpack-compose/dropdownmenu.mdx @@ -0,0 +1,62 @@ +--- +title: DropdownMenu +description: A Jetpack Compose DropdownMenu component for displaying dropdown menus. +sourceCodeUrl: 'https://github.com/expo/expo/tree/sdk-56/packages/expo-ui' +packageName: '@expo/ui' +platforms: ['android', 'expo-go'] +--- + +import APISection from '~/components/plugins/APISection'; +import { APIInstallSection } from '~/components/plugins/InstallSection'; + +Expo UI DropdownMenu matches the official Jetpack Compose [Menu API](https://developer.android.com/develop/ui/compose/components/menu) and displays a dropdown menu when a trigger element is pressed. + +## Installation + + + +## Usage + +### Basic dropdown menu + +```tsx BasicDropdownMenuExample.tsx +import { Host, DropdownMenu, Button, Text, Icon } from '@expo/ui/jetpack-compose'; +import { useState } from 'react'; + +export default function BasicDropdownMenuExample() { + const [isExpanded, setIsExpanded] = useState(false); + return ( + + setIsExpanded(false)}> + + + + + { + setIsExpanded(false); + console.log('Home pressed'); + }}> + + Home + + + + + + + + + ); +} +``` + +## API + +```tsx +import { DropdownMenu } from '@expo/ui/jetpack-compose'; +``` + + diff --git a/docs/pages/versions/v56.0.0/sdk/ui/jetpack-compose/exposeddropdownmenubox.mdx b/docs/pages/versions/v56.0.0/sdk/ui/jetpack-compose/exposeddropdownmenubox.mdx new file mode 100644 index 00000000000000..895c0fb19e3313 --- /dev/null +++ b/docs/pages/versions/v56.0.0/sdk/ui/jetpack-compose/exposeddropdownmenubox.mdx @@ -0,0 +1,86 @@ +--- +title: ExposedDropdownMenuBox +description: A Jetpack Compose ExposedDropdownMenuBox component for displaying a dropdown menu with a customizable anchor. +sourceCodeUrl: 'https://github.com/expo/expo/tree/sdk-56/packages/expo-ui' +packageName: '@expo/ui' +platforms: ['android', 'expo-go'] +--- + +import APISection from '~/components/plugins/APISection'; +import { APIInstallSection } from '~/components/plugins/InstallSection'; + +Expo UI `ExposedDropdownMenuBox` matches the official Jetpack Compose [`ExposedDropdownMenuBox`](https://kotlinlang.org/api/compose-multiplatform/material3/androidx.compose.material3/-exposed-dropdown-menu-box.html). Use the `menuAnchor()` modifier on the anchor content (typically a read-only `TextField`) and `ExposedDropdownMenu` to wrap `DropdownMenuItem` children. + +## Installation + + + +## Usage + +### Basic + +> When using an uncontrolled `TextField` as the anchor, pass `key={selected}` to force a remount when the selected value changes — `defaultValue` is only read on mount. + +```tsx BasicExposedDropdownMenuBoxExample.tsx +import { + DropdownMenuItem, + ExposedDropdownMenuBox, + ExposedDropdownMenu, + Host, + Text, + TextField, +} from '@expo/ui/jetpack-compose'; +import { menuAnchor } from '@expo/ui/jetpack-compose/modifiers'; +import { useState } from 'react'; + +const LANGUAGES = [ + { label: 'Java', value: 'java' }, + { label: 'JavaScript', value: 'js' }, + { label: 'TypeScript', value: 'ts' }, +]; + +export default function BasicExposedDropdownMenuBoxExample() { + const [selected, setSelected] = useState('java'); + const [expanded, setExpanded] = useState(false); + + const selectedLabel = LANGUAGES.find(l => l.value === selected)?.label ?? ''; + + return ( + + + + setExpanded(false)}> + {LANGUAGES.map(lang => ( + { + setSelected(lang.value); + setExpanded(false); + }}> + + {lang.label} + + + ))} + + + + ); +} +``` + +## API + +```tsx +import { ExposedDropdownMenuBox } from '@expo/ui/jetpack-compose'; +``` + + diff --git a/docs/pages/versions/v56.0.0/sdk/ui/jetpack-compose/floatingactionbutton.mdx b/docs/pages/versions/v56.0.0/sdk/ui/jetpack-compose/floatingactionbutton.mdx new file mode 100644 index 00000000000000..a27f36f6621ed5 --- /dev/null +++ b/docs/pages/versions/v56.0.0/sdk/ui/jetpack-compose/floatingactionbutton.mdx @@ -0,0 +1,181 @@ +--- +title: FloatingActionButton +description: Jetpack Compose FloatingActionButton components following Material Design 3. +sourceCodeUrl: 'https://github.com/expo/expo/tree/sdk-56/packages/expo-ui' +packageName: '@expo/ui' +platforms: ['android', 'expo-go'] +--- + +import APISection from '~/components/plugins/APISection'; +import { APIInstallSection } from '~/components/plugins/InstallSection'; + +Expo UI provides four FloatingActionButton variants matching the Material Design 3 [`FloatingActionButton`](https://developer.android.com/develop/ui/compose/components/fab) API: + +- `SmallFloatingActionButton` — a compact FAB +- `FloatingActionButton` — the standard FAB (default size) +- `LargeFloatingActionButton` — a larger FAB +- `ExtendedFloatingActionButton` — a FAB with an icon and a text label, supporting animated expand/collapse + +Each component uses slot-based children (`.Icon` and, for `ExtendedFloatingActionButton`, `.Text`) to compose content. + +> **Note:** If you need multiple action buttons in a floating toolbar, use [`HorizontalFloatingToolbar`](/versions/latest/sdk/ui/jetpack-compose/horizontalfloatingtoolbar/) instead. + +## Installation + + + +## Usage + +### Standard FloatingActionButton + +```tsx StandardFABExample.tsx +import { FloatingActionButton, Host, Icon } from '@expo/ui/jetpack-compose'; + +export default function StandardFABExample() { + return ( + + console.log('FAB pressed')}> + + + + + + ); +} +``` + +### FAB variants + +```tsx FABVariantsExample.tsx +import { + FloatingActionButton, + Host, + Icon, + LargeFloatingActionButton, + SmallFloatingActionButton, +} from '@expo/ui/jetpack-compose'; +import { View } from 'react-native'; + +export default function FABVariantsExample() { + return ( + + + {}}> + + + + + + + {}}> + + + + + + + {}}> + + + + + + + ); +} +``` + +### ExtendedFloatingActionButton + +```tsx ExtendedFABExample.tsx +import { ExtendedFloatingActionButton, Host, Icon, Text } from '@expo/ui/jetpack-compose'; +import { useState } from 'react'; + +export default function ExtendedFABExample() { + const [expanded, setExpanded] = useState(true); + + return ( + + setExpanded(v => !v)}> + + + + + Edit + + + + ); +} +``` + +### Floating over content + +Use a Compose `Box` with `align('bottomEnd')` to position the FAB over scrollable content entirely within the Compose layer. + +```tsx FloatingFABExample.tsx +import { + Box, + FloatingActionButton, + Host, + Icon, + LazyColumn, + ListItem, +} from '@expo/ui/jetpack-compose'; +import { align, fillMaxSize, fillMaxWidth, offset } from '@expo/ui/jetpack-compose/modifiers'; + +export default function FloatingFABExample() { + return ( + + + {/* ...list items... */} + + console.log('pressed')}> + + + + + + + ); +} +``` + +### Custom color + +```tsx FABCustomColorExample.tsx +import { ExtendedFloatingActionButton, Host, Icon, Text } from '@expo/ui/jetpack-compose'; + +export default function FABCustomColorExample() { + return ( + + console.log('pressed')}> + + + + + New item + + + + ); +} +``` + +## API + +```tsx +import { + SmallFloatingActionButton, + FloatingActionButton, + LargeFloatingActionButton, + ExtendedFloatingActionButton, +} from '@expo/ui/jetpack-compose'; +``` + + diff --git a/docs/pages/versions/v56.0.0/sdk/ui/jetpack-compose/flowrow.mdx b/docs/pages/versions/v56.0.0/sdk/ui/jetpack-compose/flowrow.mdx new file mode 100644 index 00000000000000..d8779497a60594 --- /dev/null +++ b/docs/pages/versions/v56.0.0/sdk/ui/jetpack-compose/flowrow.mdx @@ -0,0 +1,50 @@ +--- +title: FlowRow +description: A Jetpack Compose FlowRow component for wrapping children horizontally. +sourceCodeUrl: 'https://github.com/expo/expo/tree/sdk-56/packages/expo-ui' +packageName: '@expo/ui' +platforms: ['android', 'expo-go'] +--- + +import APISection from '~/components/plugins/APISection'; +import { APIInstallSection } from '~/components/plugins/InstallSection'; + +Expo UI FlowRow matches the official Jetpack Compose [FlowRow](https://developer.android.com/reference/kotlin/androidx/compose/foundation/layout/package-summary#FlowRow) API and arranges children in a horizontal flow that wraps to the next line when it runs out of space. + +## Installation + + + +## Usage + +`FlowRow` arranges children in a horizontal flow that wraps to the next line when it runs out of space. + +```tsx FlowRowExample.tsx +import { Host, FlowRow, Text } from '@expo/ui/jetpack-compose'; +import { paddingAll } from '@expo/ui/jetpack-compose/modifiers'; + +export default function FlowRowExample() { + const tags = ['React Native', 'Expo', 'Android', 'Jetpack Compose', 'Material 3', 'Kotlin']; + + return ( + + + {tags.map(tag => ( + {tag} + ))} + + + ); +} +``` + +## API + +```tsx +import { FlowRow } from '@expo/ui/jetpack-compose'; +``` + + diff --git a/docs/pages/versions/v56.0.0/sdk/ui/jetpack-compose/horizontalfloatingtoolbar.mdx b/docs/pages/versions/v56.0.0/sdk/ui/jetpack-compose/horizontalfloatingtoolbar.mdx new file mode 100644 index 00000000000000..299db573916b7e --- /dev/null +++ b/docs/pages/versions/v56.0.0/sdk/ui/jetpack-compose/horizontalfloatingtoolbar.mdx @@ -0,0 +1,96 @@ +--- +title: HorizontalFloatingToolbar +description: A Jetpack Compose HorizontalFloatingToolbar component for displaying a floating action bar. +sourceCodeUrl: 'https://github.com/expo/expo/tree/sdk-56/packages/expo-ui' +packageName: '@expo/ui' +platforms: ['android', 'expo-go'] +--- + +import APISection from '~/components/plugins/APISection'; +import { APIInstallSection } from '~/components/plugins/InstallSection'; + +Expo UI HorizontalFloatingToolbar wraps the official Jetpack Compose [`HorizontalFloatingToolbar`](https://kotlinlang.org/api/compose-multiplatform/material3/androidx.compose.material3/-horizontal-floating-toolbar.html) and displays a horizontal toolbar that floats above content, containing action buttons. + +> **Note:** If you only need a single floating button, use [`FloatingActionButton`](/versions/unversioned/sdk/ui/jetpack-compose/floatingactionbutton/) instead. + +## Installation + + + +## Usage + +### Floating toolbar over scrollable content + +Place the toolbar inside a `Box` with `floatingToolbarExitAlwaysScrollBehavior` to get scroll-driven hide/show behavior. Use `align('bottomCenter')` to position the toolbar at the bottom of the screen. The entire layout stays within the Compose layer — no React Native absolute positioning needed. + +```tsx FloatingToolbarExample.tsx +import { + Box, + HorizontalFloatingToolbar, + Host, + Icon, + IconButton, + LazyColumn, + ListItem, +} from '@expo/ui/jetpack-compose'; +import { align, fillMaxSize, fillMaxWidth, offset } from '@expo/ui/jetpack-compose/modifiers'; + +export default function FloatingToolbarExample() { + return ( + + + {/* ...list items... */} + + + console.log('Edit pressed')}> + + + console.log('Add pressed')}> + + + + + + ); +} +``` + +### Toolbar with FloatingActionButton + +Use `IconButton` as direct children for toolbar items, and `HorizontalFloatingToolbar.FloatingActionButton` for the primary action. + +```tsx ToolbarWithFABExample.tsx +import { Host, HorizontalFloatingToolbar, IconButton, Icon } from '@expo/ui/jetpack-compose'; + +export default function ToolbarWithFABExample() { + return ( + + + console.log('Edit pressed')}> + + + console.log('Share pressed')}> + + + console.log('Add pressed')}> + + + + + ); +} +``` + +## API + +```tsx +import { HorizontalFloatingToolbar } from '@expo/ui/jetpack-compose'; +``` + + diff --git a/docs/pages/versions/v56.0.0/sdk/ui/jetpack-compose/horizontalpager.mdx b/docs/pages/versions/v56.0.0/sdk/ui/jetpack-compose/horizontalpager.mdx new file mode 100644 index 00000000000000..5b4cda97641002 --- /dev/null +++ b/docs/pages/versions/v56.0.0/sdk/ui/jetpack-compose/horizontalpager.mdx @@ -0,0 +1,176 @@ +--- +title: HorizontalPager +description: A Jetpack Compose HorizontalPager component for swipeable pages. +sourceCodeUrl: 'https://github.com/expo/expo/tree/sdk-56/packages/expo-ui' +packageName: '@expo/ui' +platforms: ['android', 'expo-go'] +--- + +import APISection from '~/components/plugins/APISection'; +import { APIInstallSection } from '~/components/plugins/InstallSection'; + +Expo UI HorizontalPager matches Jetpack Compose's [HorizontalPager]() — a horizontally scrolling pager that snaps to individual pages. + +`HorizontalPager` does not impose its own height — give it one with the [`height`](modifiers/#heightheight) modifier or place it inside a parent with a finite height. + +## Installation + + + +## Usage + +### Uncontrolled + +The pager owns its scroll position natively. Use `initialPage` to pick the starting page, and listen for changes with `onCurrentPageChange` (fires mid-swipe as the snap target flips) or `onSettledPageChange` (fires only after the swipe settles). + +```tsx UncontrolledPagerExample.tsx +import { Box, Column, Host, HorizontalPager, Text } from '@expo/ui/jetpack-compose'; +import { background, fillMaxSize, fillMaxWidth, height } from '@expo/ui/jetpack-compose/modifiers'; +import { useState } from 'react'; + +export default function UncontrolledPagerExample() { + const [currentPage, setCurrentPage] = useState(1); + const [settledPage, setSettledPage] = useState(1); + + return ( + + + + currentPage: {currentPage} · settledPage: {settledPage} + + + + + + + + + ); +} + +function Page({ label, color }: { label: string; color: string }) { + return ( + + + {label} + + + ); +} +``` + +### Programmatic navigation + +Attach a `ref` and call `animateScrollToPage` or `scrollToPage` on it. These mirror Compose's `PagerState.animateScrollToPage` and `PagerState.scrollToPage`. + +```tsx ProgrammaticPagerExample.tsx +import { + Box, + Button, + Column, + Host, + HorizontalPager, + type HorizontalPagerHandle, + Row, + Text, +} from '@expo/ui/jetpack-compose'; +import { background, fillMaxSize, fillMaxWidth, height } from '@expo/ui/jetpack-compose/modifiers'; +import { useRef, useState } from 'react'; + +const PAGE_COUNT = 5; + +export default function ProgrammaticPagerExample() { + const pagerRef = useRef(null); + const [page, setPage] = useState(0); + + return ( + + + + Page {page + 1} / {PAGE_COUNT} + + + {Array.from({ length: PAGE_COUNT }).map((_, i) => ( + + ))} + + + + + + + + + ); +} + +const COLORS = ['#6200EE', '#03DAC5', '#FF5722', '#4CAF50', '#2196F3']; + +function Page({ label, color }: { label: string; color: string }) { + return ( + + + {label} + + + ); +} +``` + +### Page spacing and content padding + +Use `pageSpacing` to add a gap between pages (visible during swipe) and `contentPadding` to inset the pager so neighboring pages peek at rest. + +```tsx PagerLayoutExample.tsx +import { Box, Host, HorizontalPager, Text } from '@expo/ui/jetpack-compose'; +import { background, fillMaxSize, fillMaxWidth, height } from '@expo/ui/jetpack-compose/modifiers'; + +export default function PagerLayoutExample() { + return ( + + + + + + + + ); +} + +function Page({ label, color }: { label: string; color: string }) { + return ( + + + {label} + + + ); +} +``` + +## API + +```tsx +import { HorizontalPager } from '@expo/ui/jetpack-compose'; +``` + + diff --git a/docs/pages/versions/v56.0.0/sdk/ui/jetpack-compose/host.mdx b/docs/pages/versions/v56.0.0/sdk/ui/jetpack-compose/host.mdx new file mode 100644 index 00000000000000..885d08e567e460 --- /dev/null +++ b/docs/pages/versions/v56.0.0/sdk/ui/jetpack-compose/host.mdx @@ -0,0 +1,96 @@ +--- +title: Host +description: A Jetpack Compose Host component for bridging React Native and Jetpack Compose. +sourceCodeUrl: 'https://github.com/expo/expo/tree/sdk-56/packages/expo-ui' +packageName: '@expo/ui' +platforms: ['android', 'expo-go'] +--- + +import APISection from '~/components/plugins/APISection'; +import { APIInstallSection } from '~/components/plugins/InstallSection'; + +The `Host` component is the bridge between React Native and Jetpack Compose. Every Jetpack Compose component from `@expo/ui/jetpack-compose` must be wrapped in a `Host` to render correctly. + +## Installation + + + +## Usage + +### Match contents + +Use the `matchContents` prop to make the `Host` size itself to fit the content. You can pass a boolean or an object to control vertical and horizontal sizing independently. + +```tsx MatchContents.tsx +import { Host, Button } from '@expo/ui/jetpack-compose'; + +export default function MatchContents() { + return ( + + + + ); +} +``` + +> **Note:** Do not use `matchContents` on the same axis as a scrollable child (`LazyRow`, `LazyColumn`, `Carousel`, or anything using `Modifier.horizontalScroll`/`verticalScroll`). Scrollables require a finite max constraint on their scroll axis and `matchContents` propagates an unbounded one. + +The following example crashes: + +```tsx MatchContentsCrash.tsx +import { Host, LazyRow, Text } from '@expo/ui/jetpack-compose'; + +export default function MatchContentsCrash() { + return ( + + + {Array.from({ length: 5 }).map((_, i) => ( + Item {i} + ))} + + + ); +} +``` + +Either drop `matchContents` on the scroll axis or give the `Host` a finite size on that axis via `style`: + +```tsx MatchContentsFix.tsx +import { Host, LazyRow, Text } from '@expo/ui/jetpack-compose'; + +export default function MatchContentsFix() { + return ( + + + {Array.from({ length: 5 }).map((_, i) => ( + Item {i} + ))} + + + ); +} +``` + +### With style + +Apply standard React Native styles to the `Host` wrapper. + +```tsx HostWithStyle.tsx +import { Host, Button } from '@expo/ui/jetpack-compose'; + +export default function HostWithStyle() { + return ( + + + + ); +} +``` + +## API + +```tsx +import { Host } from '@expo/ui/jetpack-compose'; +``` + + diff --git a/docs/pages/versions/v56.0.0/sdk/ui/jetpack-compose/icon.mdx b/docs/pages/versions/v56.0.0/sdk/ui/jetpack-compose/icon.mdx new file mode 100644 index 00000000000000..5fb67861d6e962 --- /dev/null +++ b/docs/pages/versions/v56.0.0/sdk/ui/jetpack-compose/icon.mdx @@ -0,0 +1,78 @@ +--- +title: Icon +description: A Jetpack Compose Icon component for displaying icons. +sourceCodeUrl: 'https://github.com/expo/expo/tree/sdk-56/packages/expo-ui' +packageName: '@expo/ui' +platforms: ['android', 'expo-go'] +--- + +import APISection from '~/components/plugins/APISection'; +import { APIInstallSection } from '~/components/plugins/InstallSection'; + +An icon component for rendering icons in Jetpack Compose. We recommend downloading icons as XML vector drawables from [Material Symbols](https://fonts.google.com/icons), which is the standard approach for Android development. + +## Installation + + + +## Usage + +### Basic icon + +Use `require()` to load an XML vector drawable downloaded from [Material Symbols](https://fonts.google.com/icons). + +```tsx BasicIcon.tsx +import { Host, Icon } from '@expo/ui/jetpack-compose'; + +export default function BasicIcon() { + return ( + + + + ); +} +``` + +### Icon with tint color + +Use the `tint` prop to apply a color overlay to the icon. + +```tsx TintedIcon.tsx +import { Host, Icon } from '@expo/ui/jetpack-compose'; + +export default function TintedIcon() { + return ( + + + + ); +} +``` + +### Icon with size + +Specify a custom size in dp using the `size` prop. + +```tsx SizedIcon.tsx +import { Host, Icon } from '@expo/ui/jetpack-compose'; + +export default function SizedIcon() { + return ( + + + + ); +} +``` + +## API + +```tsx +import { Icon } from '@expo/ui/jetpack-compose'; +``` + + diff --git a/docs/pages/versions/v56.0.0/sdk/ui/jetpack-compose/iconbutton.mdx b/docs/pages/versions/v56.0.0/sdk/ui/jetpack-compose/iconbutton.mdx new file mode 100644 index 00000000000000..0d4ed5e4476d86 --- /dev/null +++ b/docs/pages/versions/v56.0.0/sdk/ui/jetpack-compose/iconbutton.mdx @@ -0,0 +1,86 @@ +--- +title: IconButton +description: Jetpack Compose IconButton components for displaying native Material3 icon buttons. +sourceCodeUrl: 'https://github.com/expo/expo/tree/sdk-56/packages/expo-ui' +packageName: '@expo/ui' +platforms: ['android', 'expo-go'] +--- + +import APISection from '~/components/plugins/APISection'; +import { APIInstallSection } from '~/components/plugins/InstallSection'; + +Expo UI provides four icon button components that match the official Jetpack Compose [IconButton API](https://developer.android.com/develop/ui/compose/components/icon-button): `IconButton`, `FilledIconButton`, `FilledTonalIconButton`, and `OutlinedIconButton`. All variants share the same props and accept composable children for content. + +## Installation + + + +## Usage + +### Basic icon button + +A standard icon button with no background, typically used for toolbar actions. + +```tsx BasicIconButtonExample.tsx +import { Host, IconButton, Icon } from '@expo/ui/jetpack-compose'; + +export default function BasicIconButtonExample() { + return ( + + alert('Pressed!')}> + + + + ); +} +``` + +### Icon button variants + +Use different icon button components to convey varying levels of emphasis. + +```tsx IconButtonVariantsExample.tsx +import { + Host, + IconButton, + FilledIconButton, + FilledTonalIconButton, + OutlinedIconButton, + Icon, + Row, +} from '@expo/ui/jetpack-compose'; + +export default function IconButtonVariantsExample() { + return ( + + + {}}> + + + {}}> + + + {}}> + + + {}}> + + + + + ); +} +``` + +## API + +```tsx +import { + IconButton, + FilledIconButton, + FilledTonalIconButton, + OutlinedIconButton, +} from '@expo/ui/jetpack-compose'; +``` + + diff --git a/docs/pages/versions/v56.0.0/sdk/ui/jetpack-compose/index.mdx b/docs/pages/versions/v56.0.0/sdk/ui/jetpack-compose/index.mdx new file mode 100644 index 00000000000000..1acb901c7ca0d9 --- /dev/null +++ b/docs/pages/versions/v56.0.0/sdk/ui/jetpack-compose/index.mdx @@ -0,0 +1,33 @@ +--- +title: Jetpack Compose +sidebar_title: Overview +description: Jetpack Compose components for building native Android interfaces with @expo/ui. +sourceCodeUrl: 'https://github.com/expo/expo/tree/sdk-56/packages/expo-ui' +packageName: '@expo/ui' +platforms: ['android', 'expo-go'] +hideTOC: true +--- + +import { APIInstallSection } from '~/components/plugins/InstallSection'; + +The Jetpack Compose components in `@expo/ui/jetpack-compose` allow you to build fully native Android interfaces using Jetpack Compose from React Native. + +## Installation + + + +## Usage + +Using a component from `@expo/ui/jetpack-compose` requires wrapping it in a [`Host`](./host) component. The `Host` is a container for Jetpack Compose views. + +```tsx +import { Host, Button } from '@expo/ui/jetpack-compose'; + +export function SaveButton() { + return ( + + + + ); +} +``` diff --git a/docs/pages/versions/v56.0.0/sdk/ui/jetpack-compose/lazycolumn.mdx b/docs/pages/versions/v56.0.0/sdk/ui/jetpack-compose/lazycolumn.mdx new file mode 100644 index 00000000000000..056d21158eaf79 --- /dev/null +++ b/docs/pages/versions/v56.0.0/sdk/ui/jetpack-compose/lazycolumn.mdx @@ -0,0 +1,104 @@ +--- +title: LazyColumn +description: A Jetpack Compose LazyColumn component for displaying scrollable lists. +sourceCodeUrl: 'https://github.com/expo/expo/tree/sdk-56/packages/expo-ui' +packageName: '@expo/ui' +platforms: ['android', 'expo-go'] +--- + +import APISection from '~/components/plugins/APISection'; +import { APIInstallSection } from '~/components/plugins/InstallSection'; + +A lazily-loaded vertical list component that only renders visible items for efficient scrolling. See the [official Jetpack Compose documentation](https://developer.android.com/develop/ui/compose/lists) for more information. + +## Installation + + + +## Usage + +### Basic lazy column + +```tsx BasicLazyColumn.tsx +import { Host, LazyColumn, ListItem } from '@expo/ui/jetpack-compose'; + +const items = Array.from({ length: 100 }, (_, i) => `Item ${i + 1}`); + +export default function BasicLazyColumn() { + return ( + + + {items.map(item => ( + + ))} + + + ); +} +``` + +### With arrangement + +Use the `verticalArrangement` prop to control how items are spaced within the list. Pass a string value like `'spaceBetween'` or an object like `{ spacedBy: 8 }` for fixed spacing in dp. + +```tsx LazyColumnArrangement.tsx +import { Host, LazyColumn, ListItem, Text } from '@expo/ui/jetpack-compose'; + +export default function LazyColumnArrangement() { + return ( + + + + + Spaced Item 1 + + + + + Spaced Item 2 + + + + + Spaced Item 3 + + + + + ); +} +``` + +### With content padding + +Use the `contentPadding` prop to add padding around the list content in dp. + +```tsx LazyColumnPadding.tsx +import { Host, LazyColumn, ListItem } from '@expo/ui/jetpack-compose'; + +export default function LazyColumnPadding() { + return ( + + + + Padded item 1 + + + Padded item 2 + + + Padded item 3 + + + + ); +} +``` + +## API + +```tsx +import { LazyColumn } from '@expo/ui/jetpack-compose'; +``` + + diff --git a/docs/pages/versions/v56.0.0/sdk/ui/jetpack-compose/lazyrow.mdx b/docs/pages/versions/v56.0.0/sdk/ui/jetpack-compose/lazyrow.mdx new file mode 100644 index 00000000000000..5479d897ad505c --- /dev/null +++ b/docs/pages/versions/v56.0.0/sdk/ui/jetpack-compose/lazyrow.mdx @@ -0,0 +1,86 @@ +--- +title: LazyRow +description: A Jetpack Compose LazyRow component for displaying horizontally scrolling lists. +sourceCodeUrl: 'https://github.com/expo/expo/tree/sdk-56/packages/expo-ui' +packageName: '@expo/ui' +platforms: ['android', 'expo-go'] +--- + +import APISection from '~/components/plugins/APISection'; +import { APIInstallSection } from '~/components/plugins/InstallSection'; + +A lazily-loaded horizontal list component that only renders visible items for efficient scrolling. See the [official Jetpack Compose documentation](https://developer.android.com/develop/ui/compose/lists) for more information. + +## Installation + + + +## Usage + +### Basic lazy row + +```tsx BasicLazyRow.tsx +import { Host, LazyRow, Text } from '@expo/ui/jetpack-compose'; + +const items = Array.from({ length: 100 }, (_, i) => `Item ${i + 1}`); + +export default function BasicLazyRow() { + return ( + + + {items.map(item => ( + {item} + ))} + + + ); +} +``` + +### With arrangement + +Use the `horizontalArrangement` prop to control how items are spaced within the list. Pass a string value like `'spaceBetween'` or an object like `{ spacedBy: 8 }` for fixed spacing in dp. + +```tsx LazyRowArrangement.tsx +import { Host, LazyRow, Text } from '@expo/ui/jetpack-compose'; + +export default function LazyRowArrangement() { + return ( + + + Spaced item 1 + Spaced item 2 + Spaced item 3 + + + ); +} +``` + +### With content padding + +Use the `contentPadding` prop to add padding around the list content in dp. + +```tsx LazyRowPadding.tsx +import { Host, LazyRow, Text } from '@expo/ui/jetpack-compose'; + +export default function LazyRowPadding() { + return ( + + + Padded item 1 + Padded item 2 + Padded item 3 + + + ); +} +``` + +## API + +```tsx +import { LazyRow } from '@expo/ui/jetpack-compose'; +``` + + diff --git a/docs/pages/versions/v56.0.0/sdk/ui/jetpack-compose/listitem.mdx b/docs/pages/versions/v56.0.0/sdk/ui/jetpack-compose/listitem.mdx new file mode 100644 index 00000000000000..d552eb8dbe1ad7 --- /dev/null +++ b/docs/pages/versions/v56.0.0/sdk/ui/jetpack-compose/listitem.mdx @@ -0,0 +1,120 @@ +--- +title: ListItem +description: A Jetpack Compose ListItem component for displaying structured list entries. +sourceCodeUrl: 'https://github.com/expo/expo/tree/sdk-56/packages/expo-ui' +packageName: '@expo/ui' +platforms: ['android', 'expo-go'] +--- + +import APISection from '~/components/plugins/APISection'; +import { APIInstallSection } from '~/components/plugins/InstallSection'; + +Expo UI ListItem matches the official Jetpack Compose [`ListItem`]() API for structured list entries with headline, supporting, overline, leading, and trailing content slots. + +## Installation + + + +## Usage + +### Basic list item + +```tsx BasicListItem.tsx +import { Host, ListItem, Text } from '@expo/ui/jetpack-compose'; + +export default function BasicListItem() { + return ( + + + + Settings + + + + ); +} +``` + +### With compound components + +Use compound components for rich content in each position. + +```tsx ListItemWithCompoundComponent.tsx +import { Host, ListItem, Icon, Text } from '@expo/ui/jetpack-compose'; + +export default function ListItemWithSlots() { + return ( + + + + Notifications + + + ACCOUNT + + + Manage notification preferences + + + + + + + + + + ); +} +``` + +### Clickable list item + +Use the `clickable` modifier to handle tap interactions. + +```tsx ClickableListItem.tsx +import { Host, ListItem, Text } from '@expo/ui/jetpack-compose'; +import { clickable } from '@expo/ui/jetpack-compose/modifiers'; + +export default function ClickableListItem() { + return ( + + console.log('Tapped!'))]}> + + Tap me + + + + ); +} +``` + +### Custom headline content + +Use `ListItem.HeadlineContent` for composable headline content like rows with icons. + +```tsx ListItemCustomHeadline.tsx +import { Host, ListItem, Text, Row, Icon } from '@expo/ui/jetpack-compose'; + +export default function ListItemCustomHeadline() { + return ( + + + + + Premium Feature + + + + + + ); +} +``` + +## API + +```tsx +import { ListItem } from '@expo/ui/jetpack-compose'; +``` + + diff --git a/docs/pages/versions/v56.0.0/sdk/ui/jetpack-compose/metadata.json b/docs/pages/versions/v56.0.0/sdk/ui/jetpack-compose/metadata.json new file mode 100644 index 00000000000000..9c9d6402df1d77 --- /dev/null +++ b/docs/pages/versions/v56.0.0/sdk/ui/jetpack-compose/metadata.json @@ -0,0 +1,4 @@ +{ + "sidebarTitle": "Jetpack Compose", + "expanded": false +} diff --git a/docs/pages/versions/v56.0.0/sdk/ui/jetpack-compose/modifiers.mdx b/docs/pages/versions/v56.0.0/sdk/ui/jetpack-compose/modifiers.mdx new file mode 100644 index 00000000000000..c68d2edc327c8f --- /dev/null +++ b/docs/pages/versions/v56.0.0/sdk/ui/jetpack-compose/modifiers.mdx @@ -0,0 +1,460 @@ +--- +title: Modifiers +description: Jetpack Compose layout modifiers for @expo/ui components. +sourceCodeUrl: 'https://github.com/expo/expo/tree/sdk-56/packages/expo-ui' +packageName: '@expo/ui' +platforms: ['android', 'expo-go'] +--- + +import APISection from '~/components/plugins/APISection'; +import { APIInstallSection } from '~/components/plugins/InstallSection'; + +Jetpack Compose modifiers allow you to customize the layout, appearance, and behavior of UI components. Modifiers are the Compose equivalent of style properties — they control sizing, padding, backgrounds, interactions, and more. + +## Installation + + + +## Usage + +Modifiers are applied to components using the `modifiers` prop with an array syntax. You can combine multiple modifiers to create complex styling and behavior. Modifiers are applied in the order they appear in the array, which can affect the final result (for example, applying `padding` before `background` produces different results than the reverse). + +```tsx +import { Button, Host } from '@expo/ui/jetpack-compose'; +import { + paddingAll, + fillMaxWidth, + background, + border, + shadow, + clickable, +} from '@expo/ui/jetpack-compose/modifiers'; + +function ModifiersExample() { + return ( + + {/* Basic styling modifiers */} + + + {/* Complex combination with border and shadow */} + + + ); +} +``` + +## Padding + +Control the spacing around a component's content. + +### `paddingAll(all)` + +Applies equal padding on all four sides. + +| Parameter | Type | Description | +| --------- | -------- | -------------------- | +| `all` | `number` | Padding value in dp. | + +```tsx +import { paddingAll } from '@expo/ui/jetpack-compose/modifiers'; + +; +``` + +### `padding(start, top, end, bottom)` + +Applies individual padding to each side. + +| Parameter | Type | Description | +| --------- | -------- | ------------------------- | +| `start` | `number` | Left/start padding in dp. | +| `top` | `number` | Top padding in dp. | +| `end` | `number` | Right/end padding in dp. | +| `bottom` | `number` | Bottom padding in dp. | + +```tsx +import { padding } from '@expo/ui/jetpack-compose/modifiers'; + +; +``` + +## Size + +Control the dimensions of a component. + +### `size(width, height)` + +Sets exact dimensions for the component. + +| Parameter | Type | Description | +| --------- | -------- | ------------- | +| `width` | `number` | Width in dp. | +| `height` | `number` | Height in dp. | + +```tsx +import { size } from '@expo/ui/jetpack-compose/modifiers'; + +; +``` + +### `fillMaxSize(fraction?)` + +Fills all available space in both dimensions. + +| Parameter | Type | Description | +| ---------- | -------- | ----------------------------------------------------- | +| `fraction` | `number` | Fraction of available space (0.0–1.0). Default `1.0`. | + +```tsx +import { fillMaxSize } from '@expo/ui/jetpack-compose/modifiers'; + + + +``` + +### `fillMaxWidth(fraction?)` + +Fills available width. + +| Parameter | Type | Description | +| ---------- | -------- | ----------------------------------------------------- | +| `fraction` | `number` | Fraction of available width (0.0–1.0). Default `1.0`. | + +```tsx +import { fillMaxWidth } from '@expo/ui/jetpack-compose/modifiers'; + +; +``` + +### `fillMaxHeight(fraction?)` + +Fills available height. + +| Parameter | Type | Description | +| ---------- | -------- | ------------------------------------------------------ | +| `fraction` | `number` | Fraction of available height (0.0–1.0). Default `1.0`. | + +### `width(value)` + +Sets an exact width. + +| Parameter | Type | Description | +| --------- | -------- | ------------ | +| `value` | `number` | Width in dp. | + +### `height(value)` + +Sets an exact height. + +| Parameter | Type | Description | +| --------- | -------- | ------------- | +| `value` | `number` | Height in dp. | + +### `wrapContentWidth(alignment?)` + +Sizes the component to wrap its content width. + +| Parameter | Type | Description | +| ----------- | -------- | ------------------------------------ | +| `alignment` | `string` | Horizontal alignment of the content. | + +### `wrapContentHeight(alignment?)` + +Sizes the component to wrap its content height. + +| Parameter | Type | Description | +| ----------- | -------- | ---------------------------------- | +| `alignment` | `string` | Vertical alignment of the content. | + +## Position + +Control the position of a component relative to its natural placement. + +### `offset(x, y)` + +Offsets the component from its natural position without affecting the layout of surrounding components. + +| Parameter | Type | Description | +| --------- | -------- | ------------------------ | +| `x` | `number` | Horizontal offset in dp. | +| `y` | `number` | Vertical offset in dp. | + +```tsx +import { offset } from '@expo/ui/jetpack-compose/modifiers'; + +; +``` + +## Appearance + +Control the visual appearance of a component. + +### `background(color)` + +Sets the background color. + +| Parameter | Type | Description | +| --------- | -------- | ------------------------------ | +| `color` | `string` | Background color (hex string). | + +```tsx +import { background } from '@expo/ui/jetpack-compose/modifiers'; + +; +``` + +### `border(borderWidth, borderColor)` + +Adds a border around the component. + +| Parameter | Type | Description | +| ------------- | -------- | -------------------------- | +| `borderWidth` | `number` | Border width in dp. | +| `borderColor` | `string` | Border color (hex string). | + +```tsx +import { border } from '@expo/ui/jetpack-compose/modifiers'; + +; +``` + +### `shadow(elevation)` + +Adds an elevation shadow beneath the component. + +| Parameter | Type | Description | +| ----------- | -------- | ----------------------- | +| `elevation` | `number` | Shadow elevation in dp. | + +```tsx +import { shadow } from '@expo/ui/jetpack-compose/modifiers'; + +; +``` + +### `alpha(alpha)` + +Controls the opacity of the component. + +| Parameter | Type | Description | +| --------- | -------- | ------------------------ | +| `alpha` | `number` | Opacity value (0.0–1.0). | + +```tsx +import { alpha } from '@expo/ui/jetpack-compose/modifiers'; + +; +``` + +### `blur(radius)` + +Applies a blur effect to the component. + +| Parameter | Type | Description | +| --------- | -------- | ------------------ | +| `radius` | `number` | Blur radius in dp. | + +```tsx +import { blur } from '@expo/ui/jetpack-compose/modifiers'; + +; +``` + +## Transform + +Apply visual transformations to a component. + +### `rotate(degrees)` + +Rotates the component. + +| Parameter | Type | Description | +| --------- | -------- | -------------------------- | +| `degrees` | `number` | Rotation angle in degrees. | + +```tsx +import { rotate } from '@expo/ui/jetpack-compose/modifiers'; + +; +``` + +### `zIndex(index)` + +Controls the drawing order of overlapping components. + +| Parameter | Type | Description | +| --------- | -------- | ------------ | +| `index` | `number` | Layer index. | + +```tsx +import { zIndex } from '@expo/ui/jetpack-compose/modifiers'; + +; +``` + +## Animation + +Animate layout changes within a component. + +### `animateContentSize(dampingRatio?, stiffness?)` + +Animates size changes of the component's content using a spring animation. + +| Parameter | Type | Description | +| -------------- | -------- | ------------------------------------------- | +| `dampingRatio` | `number` | Spring damping ratio. Controls bounciness. | +| `stiffness` | `number` | Spring stiffness. Controls animation speed. | + +```tsx +import { animateContentSize } from '@expo/ui/jetpack-compose/modifiers'; + + + +``` + +## Layout + +Control how a component is sized and positioned within its parent container. + +### `weight(weight)` + +Assigns a flexible weight to a component inside a `Row` or `Column`, distributing available space proportionally among weighted children. + +| Parameter | Type | Description | +| --------- | -------- | -------------- | +| `weight` | `number` | Weight factor. | + +```tsx +import { weight } from '@expo/ui/jetpack-compose/modifiers'; + +// In a Row, the first button takes 2/3 and the second takes 1/3 + + +``` + +### `align(alignment)` + +Sets the alignment of the component within its parent container. + +| Parameter | Type | Description | +| ----------- | -------- | ------------------------------- | +| `alignment` | `string` | Alignment within the container. | + +### `matchParentSize()` + +Sizes the component to match the size of its parent `Box`. Unlike `fillMaxSize`, this does not affect the parent's measurement. + +```tsx +import { matchParentSize } from '@expo/ui/jetpack-compose/modifiers'; + +; +``` + +## Interaction + +Add user interaction handlers to a component. + +### `clickable(handler)` + +Makes the component respond to click events. + +| Parameter | Type | Description | +| --------- | ------------ | -------------------------- | +| `handler` | `() => void` | Callback invoked on click. | + +```tsx +import { clickable } from '@expo/ui/jetpack-compose/modifiers'; + +; +``` + +### `selectable(selected, handler)` + +Makes the component selectable, similar to a radio button. + +| Parameter | Type | Description | +| ---------- | ------------ | ---------------------------------------------- | +| `selected` | `boolean` | Whether the component is currently selected. | +| `handler` | `() => void` | Callback invoked when selection state changes. | + +```tsx +import { selectable } from '@expo/ui/jetpack-compose/modifiers'; + +; +``` + +## Clipping + +Clip a component's content to a specific shape. + +### `clip(shape)` + +Clips the component to the given shape. Content outside the shape boundary is not drawn. + +| Parameter | Type | Description | +| --------- | ------- | --------------------- | +| `shape` | `Shape` | The shape to clip to. | + +#### Available shapes + +| Shape | Description | +| ------------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| `Shapes.Rectangle` | A rectangle with no rounded corners. | +| `Shapes.Circle` | A perfect circle. | +| `Shapes.RoundedCorner(radius)` | A rectangle with uniform rounded corners. Pass a `number` for equal radius or an object `{ topStart, topEnd, bottomStart, bottomEnd }` for individual corner radii. | +| `Shapes.CutCorner(radius)` | A rectangle with cut (chamfered) corners. Accepts the same radius options as `RoundedCorner`. | +| `Shapes.Material.Cookie4Sided` | Material Design cookie shape with 4 sides. | +| `Shapes.Material.Cookie6Sided` | Material Design cookie shape with 6 sides. | + +```tsx +import { clip } from '@expo/ui/jetpack-compose/modifiers'; +import { Shapes } from '@expo/ui/jetpack-compose/modifiers'; + +// Circular clipping + + +// Rounded corners with uniform radius + + +// Rounded corners with individual radii + + +// Cut corners + +``` + +## Utility + +### `testID(tag)` + +Assigns a test identifier to the component for use in UI testing. + +| Parameter | Type | Description | +| --------- | -------- | ------------ | +| `tag` | `string` | The test ID. | + +```tsx +import { testID } from '@expo/ui/jetpack-compose/modifiers'; + +; +``` + +## API + +{/* prettier-ignore */} +```tsx +import { paddingAll, padding, size, fillMaxWidth, background, clickable, clip, Shapes } from '@expo/ui/jetpack-compose/modifiers'; +``` + + diff --git a/docs/pages/versions/v56.0.0/sdk/ui/jetpack-compose/progress.mdx b/docs/pages/versions/v56.0.0/sdk/ui/jetpack-compose/progress.mdx new file mode 100644 index 00000000000000..081819ac6dbb2c --- /dev/null +++ b/docs/pages/versions/v56.0.0/sdk/ui/jetpack-compose/progress.mdx @@ -0,0 +1,131 @@ +--- +title: Progress Indicators +description: Jetpack Compose progress indicator components for displaying operation status. +sourceCodeUrl: 'https://github.com/expo/expo/tree/sdk-56/packages/expo-ui' +packageName: '@expo/ui' +platforms: ['android', 'expo-go'] +--- + +import APISection from '~/components/plugins/APISection'; +import { APIInstallSection } from '~/components/plugins/InstallSection'; + +Expo UI Progress Indicators match the official Jetpack Compose [Progress Indicator API](https://developer.android.com/develop/ui/compose/components/progress). + +## Installation + + + +## Usage + +### Linear progress + +A horizontal bar that fills to indicate progress. Provide a `progress` value between `0` and `1` for determinate mode. + +```tsx LinearExample.tsx +import { Host, LinearProgressIndicator } from '@expo/ui/jetpack-compose'; + +export default function LinearExample() { + return ( + + + + ); +} +``` + +### Circular progress + +A spinning circle whose stroke grows to indicate progress. + +```tsx CircularExample.tsx +import { Host, CircularProgressIndicator } from '@expo/ui/jetpack-compose'; + +export default function CircularExample() { + return ( + + + + ); +} +``` + +### Indeterminate + +Omit the `progress` prop to animate continuously without indicating a specific completion level. + +```tsx IndeterminateExample.tsx +import { + CircularProgressIndicator, + CircularWavyProgressIndicator, + Column, + Host, + LinearProgressIndicator, + LinearWavyProgressIndicator, +} from '@expo/ui/jetpack-compose'; + +export default function IndeterminateExample() { + return ( + + + + + + + + + ); +} +``` + +### Custom colors + +Use `color` for the indicator and `trackColor` for the background track. + +```tsx ColorsExample.tsx +import { Host, CircularProgressIndicator } from '@expo/ui/jetpack-compose'; + +export default function ColorsExample() { + return ( + + + + ); +} +``` + +### Wavy variants + +`LinearWavyProgressIndicator` and `CircularWavyProgressIndicator` add an expressive wave animation from Material 3 Expressive. + +```tsx WavyExample.tsx +import { + Host, + LinearWavyProgressIndicator, + CircularWavyProgressIndicator, + Column, +} from '@expo/ui/jetpack-compose'; + +export default function WavyExample() { + return ( + + + + + + + ); +} +``` + +## API + +```tsx +import { + LinearProgressIndicator, + CircularProgressIndicator, + LinearWavyProgressIndicator, + CircularWavyProgressIndicator, +} from '@expo/ui/jetpack-compose'; +``` + + diff --git a/docs/pages/versions/v56.0.0/sdk/ui/jetpack-compose/pulltorefreshbox.mdx b/docs/pages/versions/v56.0.0/sdk/ui/jetpack-compose/pulltorefreshbox.mdx new file mode 100644 index 00000000000000..8dedb7d674b6fc --- /dev/null +++ b/docs/pages/versions/v56.0.0/sdk/ui/jetpack-compose/pulltorefreshbox.mdx @@ -0,0 +1,111 @@ +--- +title: PullToRefreshBox +description: A Jetpack Compose PullToRefreshBox component for pull-to-refresh interactions. +sourceCodeUrl: 'https://github.com/expo/expo/tree/sdk-56/packages/expo-ui' +packageName: '@expo/ui' +platforms: ['android', 'expo-go'] +--- + +import APISection from '~/components/plugins/APISection'; +import { APIInstallSection } from '~/components/plugins/InstallSection'; + +Expo UI PullToRefreshBox matches the official Jetpack Compose [PullToRefreshBox]() API. It wraps scrollable content and shows a refresh indicator when pulled. + +## Installation + + + +## Usage + +### Basic pull to refresh + +Wrap scrollable content in a `PullToRefreshBox` to add pull-to-refresh behavior. + +```tsx BasicPullToRefresh.tsx +import { useState, useCallback } from 'react'; +import { Host, PullToRefreshBox, LazyColumn, ListItem } from '@expo/ui/jetpack-compose'; + +export default function BasicPullToRefresh() { + const [refreshing, setRefreshing] = useState(false); + + const onRefresh = useCallback(() => { + setRefreshing(true); + setTimeout(() => { + setRefreshing(false); + }, 2000); + }, []); + + return ( + + + + + Item 1 + + + Item 2 + + + Item 3 + + + Item 4 + + + Item 5 + + + + + ); +} +``` + +### Custom indicator colors + +Use the `indicator` prop to customize the spinner and container colors. + +```tsx CustomIndicatorColors.tsx +import { useState, useCallback } from 'react'; +import { Host, PullToRefreshBox, LazyColumn, ListItem } from '@expo/ui/jetpack-compose'; + +export default function CustomIndicatorColors() { + const [refreshing, setRefreshing] = useState(false); + + const onRefresh = useCallback(() => { + setRefreshing(true); + setTimeout(() => { + setRefreshing(false); + }, 2000); + }, []); + + return ( + + + + + Item 1 + + + Item 2 + + + Item 3 + + + + + ); +} +``` + +## API + +```tsx +import { PullToRefreshBox } from '@expo/ui/jetpack-compose'; +``` + + diff --git a/docs/pages/versions/v56.0.0/sdk/ui/jetpack-compose/radiobutton.mdx b/docs/pages/versions/v56.0.0/sdk/ui/jetpack-compose/radiobutton.mdx new file mode 100644 index 00000000000000..12aa34cbfcd634 --- /dev/null +++ b/docs/pages/versions/v56.0.0/sdk/ui/jetpack-compose/radiobutton.mdx @@ -0,0 +1,91 @@ +--- +title: RadioButton +description: A Jetpack Compose RadioButton component for single-selection controls. +sourceCodeUrl: 'https://github.com/expo/expo/tree/sdk-56/packages/expo-ui' +packageName: '@expo/ui' +platforms: ['android', 'expo-go'] +--- + +import APISection from '~/components/plugins/APISection'; +import { APIInstallSection } from '~/components/plugins/InstallSection'; + +A radio button component for selecting a single option from a set. Maps to the official Jetpack Compose [RadioButton](https://developer.android.com/develop/ui/compose/components/radio-button) API. + +## Installation + + + +## Usage + +### Basic radio button + +A standalone radio button with an `onClick` handler. + +```tsx BasicRadioButton.tsx +import { useState } from 'react'; +import { Host, RadioButton } from '@expo/ui/jetpack-compose'; + +export default function BasicRadioButton() { + const [selected, setSelected] = useState(false); + + return ( + + setSelected(!selected)} /> + + ); +} +``` + +### Radio group (recommended) + +The recommended pattern for a radio group follows the [Compose accessibility guidelines](https://developer.android.com/develop/ui/compose/components/radio-button): + +- Wrap the group in a `Column` with the `selectableGroup()` modifier so screen readers treat the options as a group. +- Apply the `selectable` modifier with `role: 'radioButton'` on each `Row`, making the entire row (including the label) tappable. +- Pass no `onClick` to the `RadioButton` itself, the row handles the interaction. This provides a larger touch target. + +```tsx RadioGroup.tsx +import { useState } from 'react'; +import { Host, Column, Row, RadioButton, Text } from '@expo/ui/jetpack-compose'; +import { + selectable, + selectableGroup, + fillMaxWidth, + height, + padding, +} from '@expo/ui/jetpack-compose/modifiers'; + +export default function RadioGroup() { + const [selectedOption, setSelectedOption] = useState('Calls'); + const options = ['Calls', 'Missed', 'Friends']; + + return ( + + + {options.map(label => ( + setSelectedOption(label), 'radioButton'), + padding(16, 0, 16, 0), + ]}> + + {label} + + ))} + + + ); +} +``` + +## API + +```tsx +import { RadioButton } from '@expo/ui/jetpack-compose'; +``` + + diff --git a/docs/pages/versions/v56.0.0/sdk/ui/jetpack-compose/rnhostview.mdx b/docs/pages/versions/v56.0.0/sdk/ui/jetpack-compose/rnhostview.mdx new file mode 100644 index 00000000000000..26bf1c0bbbe31f --- /dev/null +++ b/docs/pages/versions/v56.0.0/sdk/ui/jetpack-compose/rnhostview.mdx @@ -0,0 +1,221 @@ +--- +title: RNHostView +description: A component that enables React Native views inside Jetpack Compose. +sourceCodeUrl: 'https://github.com/expo/expo/tree/sdk-56/packages/expo-ui' +packageName: '@expo/ui' +platforms: ['android', 'expo-go'] +--- + +import APISection from '~/components/plugins/APISection'; +import { APIInstallSection } from '~/components/plugins/InstallSection'; + +A component that enables proper layout behavior when React Native views are rendered inside Jetpack Compose components. It syncs layout information from Jetpack Compose back to React Native's Yoga layout system by updating the shadow node size. + +When React Native views are placed inside Jetpack Compose components like [`ModalBottomSheet`](bottomsheet), [`Card`](card), [`Row`](row), [`Column`](column) and so on, the layout systems need to communicate. `RNHostView` bridges this gap: + +- **With [`matchContents`](#matchcontents)**: The shadow node size is set to match the child React Native view's intrinsic size, allowing the Jetpack Compose parent to size itself based on the React Native content. +- **Without `matchContents`**: The shadow node size is set to match the parent Jetpack Compose view's size, allowing the React Native content to fill the available space (useful for `flex: 1` layouts). + +## Installation + + + +## Usage + +### Basic usage with matchContents + +Use `matchContents` when you want the Jetpack Compose parent to size itself based on the React Native content. + +```tsx RNHostView with matchContents +import { useState } from 'react'; +import { Pressable, Text as RNText, View } from 'react-native'; +import { Host, Card, Column, Row, RNHostView, Text } from '@expo/ui/jetpack-compose'; +import { fillMaxWidth, padding } from '@expo/ui/jetpack-compose/modifiers'; + +function Example() { + const [counter, setCounter] = useState(0); + + return ( + + + + Mixing RN Components with Compose + + + setCounter(prev => prev - 1)} + style={{ + height: 50, + width: 50, + borderRadius: 100, + justifyContent: 'center', + alignItems: 'center', + backgroundColor: '#9B59B6', + }}> + - + + + {counter} + + setCounter(prev => prev + 1)} + style={{ + height: 50, + width: 50, + borderRadius: 100, + justifyContent: 'center', + alignItems: 'center', + backgroundColor: '#9B59B6', + }}> + + + + + + + + + ); +} +``` + +### Flexible content without matchContents + +When using `flex: 1` in your React Native content, omit the `matchContents` prop so the content fills the available Jetpack Compose space. + +```tsx RNHostView with flex content +import { Text as RNText, View } from 'react-native'; +import { Host, Card, Column, Row, RNHostView, Text } from '@expo/ui/jetpack-compose'; +import { fillMaxWidth, padding, size } from '@expo/ui/jetpack-compose/modifiers'; + +function Example() { + return ( + + + + RN components with flex: 1 children + + + + + + + + + ); +} +``` + +### Usage with ModalBottomSheet + +`RNHostView` works well inside [`ModalBottomSheet`](/versions/latest/sdk/ui/jetpack-compose/bottomsheet) to display interactive React Native content. + +```tsx RNHostView in ModalBottomSheet +import { useRef, useState } from 'react'; +import { Pressable, Text as RNText, View } from 'react-native'; +import { Host, ModalBottomSheet, Button, Column, RNHostView, Text } from '@expo/ui/jetpack-compose'; +import type { ModalBottomSheetRef } from '@expo/ui/jetpack-compose'; +import { padding } from '@expo/ui/jetpack-compose/modifiers'; + +function Example() { + const [visible, setVisible] = useState(false); + const sheetRef = useRef(null); + + const hideSheet = async () => { + await sheetRef.current?.hide(); + setVisible(false); + }; + + return ( + + + {visible && ( + setVisible(false)}> + + Mixing Compose + RN in a Bottom Sheet + + + + React Native Content + + + Close + + + + + + )} + + ); +} +``` + +### Flexible React Native content in a bottom sheet + +Use `RNHostView` without `matchContents` to let the React Native view fill the remaining space inside the sheet. Combine with a `height` modifier on the parent `Column` to control the sheet size. + +```tsx RNHostView flex in ModalBottomSheet +import { useRef, useState } from 'react'; +import { Text as RNText, View } from 'react-native'; +import { Host, ModalBottomSheet, Button, Column, RNHostView, Text } from '@expo/ui/jetpack-compose'; +import type { ModalBottomSheetRef } from '@expo/ui/jetpack-compose'; +import { height, padding } from '@expo/ui/jetpack-compose/modifiers'; + +function Example() { + const [visible, setVisible] = useState(false); + const sheetRef = useRef(null); + + return ( + + + {visible && ( + setVisible(false)} + skipPartiallyExpanded> + + + + + React Native Content (flex: 1) + + + + + + )} + + ); +} +``` + +## API + +```tsx +import { RNHostView } from '@expo/ui/jetpack-compose'; +``` + + diff --git a/docs/pages/versions/v56.0.0/sdk/ui/jetpack-compose/row.mdx b/docs/pages/versions/v56.0.0/sdk/ui/jetpack-compose/row.mdx new file mode 100644 index 00000000000000..686ebe189af079 --- /dev/null +++ b/docs/pages/versions/v56.0.0/sdk/ui/jetpack-compose/row.mdx @@ -0,0 +1,48 @@ +--- +title: Row +description: A Jetpack Compose Row component for placing children horizontally. +sourceCodeUrl: 'https://github.com/expo/expo/tree/sdk-56/packages/expo-ui' +packageName: '@expo/ui' +platforms: ['android', 'expo-go'] +--- + +import APISection from '~/components/plugins/APISection'; +import { APIInstallSection } from '~/components/plugins/InstallSection'; + +Expo UI Row matches the official Jetpack Compose [Row](https://developer.android.com/reference/kotlin/androidx/compose/foundation/layout/package-summary#Row) API and places children horizontally with configurable arrangement and alignment. + +## Installation + + + +## Usage + +`Row` places children horizontally. Use `horizontalArrangement` and `verticalAlignment` to control spacing and alignment. + +```tsx RowExample.tsx +import { Host, Row, Text } from '@expo/ui/jetpack-compose'; +import { fillMaxWidth, height } from '@expo/ui/jetpack-compose/modifiers'; + +export default function RowExample() { + return ( + + + Item 1 + Item 2 + Item 3 + + + ); +} +``` + +## API + +```tsx +import { Row } from '@expo/ui/jetpack-compose'; +``` + + diff --git a/docs/pages/versions/v56.0.0/sdk/ui/jetpack-compose/searchbar.mdx b/docs/pages/versions/v56.0.0/sdk/ui/jetpack-compose/searchbar.mdx new file mode 100644 index 00000000000000..30154db493e0c0 --- /dev/null +++ b/docs/pages/versions/v56.0.0/sdk/ui/jetpack-compose/searchbar.mdx @@ -0,0 +1,64 @@ +--- +title: SearchBar +description: A Jetpack Compose SearchBar component for search input functionality. +sourceCodeUrl: 'https://github.com/expo/expo/tree/sdk-56/packages/expo-ui' +packageName: '@expo/ui' +platforms: ['android', 'expo-go'] +--- + +import APISection from '~/components/plugins/APISection'; +import { APIInstallSection } from '~/components/plugins/InstallSection'; + +Expo UI SearchBar matches the official Jetpack Compose [Search](https://developer.android.com/develop/ui/compose/components/search-bar) API and provides a search input with support for placeholder text and expanded full-screen search. + +## Installation + + + +## Usage + +### Basic search bar + +```tsx BasicSearchBarExample.tsx +import { useState } from 'react'; +import { Host, SearchBar } from '@expo/ui/jetpack-compose'; + +export default function BasicSearchBarExample() { + const [query, setQuery] = useState(''); + + return ( + + setQuery(searchText)} /> + + ); +} +``` + +### Search bar with placeholder + +Use the `SearchBar.Placeholder` sub-component to display hint text when the search field is empty. + +```tsx SearchBarPlaceholderExample.tsx +import { useState } from 'react'; +import { Host, SearchBar } from '@expo/ui/jetpack-compose'; + +export default function SearchBarPlaceholderExample() { + const [query, setQuery] = useState(''); + + return ( + + setQuery(searchText)}> + Search items... + + + ); +} +``` + +## API + +```tsx +import { SearchBar } from '@expo/ui/jetpack-compose'; +``` + + diff --git a/docs/pages/versions/v56.0.0/sdk/ui/jetpack-compose/segmentedbutton.mdx b/docs/pages/versions/v56.0.0/sdk/ui/jetpack-compose/segmentedbutton.mdx new file mode 100644 index 00000000000000..7f96abc3a015e1 --- /dev/null +++ b/docs/pages/versions/v56.0.0/sdk/ui/jetpack-compose/segmentedbutton.mdx @@ -0,0 +1,153 @@ +--- +title: SegmentedButton +description: Jetpack Compose Segmented Button components for single or multi-choice selection. +sourceCodeUrl: 'https://github.com/expo/expo/tree/sdk-56/packages/expo-ui' +packageName: '@expo/ui' +platforms: ['android', 'expo-go'] +--- + +import APISection from '~/components/plugins/APISection'; +import { APIInstallSection } from '~/components/plugins/InstallSection'; + +Segmented buttons let app users choose from a small set of options displayed side by side in a row. They map to the official Jetpack Compose [Segmented Button](https://developer.android.com/develop/ui/compose/components/segmented-button) API. + +There are two container types: + +- **`SingleChoiceSegmentedButtonRow`**: only one button can be selected at a time (like radio buttons). +- **`MultiChoiceSegmentedButtonRow`**: multiple buttons can be toggled independently (like checkboxes). + +## Installation + + + +## Usage + +### Single-choice segmented button + +Use `SingleChoiceSegmentedButtonRow` when only one option can be active at a time. Each `SegmentedButton` takes `selected` and `onClick` props. + +```tsx SingleChoiceExample.tsx +import { useState } from 'react'; +import { + Host, + SingleChoiceSegmentedButtonRow, + SegmentedButton, + Text, +} from '@expo/ui/jetpack-compose'; + +export default function SingleChoiceExample() { + const [selectedIndex, setSelectedIndex] = useState(0); + const options = ['Day', 'Week', 'Month', 'Year']; + + return ( + + + {options.map((label, index) => ( + setSelectedIndex(index)}> + + {label} + + + ))} + + + ); +} +``` + +### Multi-choice segmented button + +Use `MultiChoiceSegmentedButtonRow` when multiple options can be toggled independently. Each `SegmentedButton` takes `checked` and `onCheckedChange` props. + +```tsx MultiChoiceExample.tsx +import { useState } from 'react'; +import { + Host, + MultiChoiceSegmentedButtonRow, + SegmentedButton, + Text, +} from '@expo/ui/jetpack-compose'; + +export default function MultiChoiceExample() { + const [checkedItems, setCheckedItems] = useState([false, false, false, false]); + const options = ['Wi-Fi', 'Bluetooth', 'NFC', 'GPS']; + + return ( + + + {options.map((label, index) => ( + { + setCheckedItems(prev => { + const next = [...prev]; + next[index] = checked; + return next; + }); + }}> + + {label} + + + ))} + + + ); +} +``` + +### Custom colors + +Use the `colors` prop on `SegmentedButton` to customize its appearance across active, inactive, and disabled states. + +```tsx CustomColorsExample.tsx +import { useState } from 'react'; +import { + Host, + SingleChoiceSegmentedButtonRow, + SegmentedButton, + Text, +} from '@expo/ui/jetpack-compose'; + +export default function CustomColorsExample() { + const [selectedIndex, setSelectedIndex] = useState(0); + const options = ['$', '$$', '$$$', '$$$$']; + + return ( + + + {options.map((label, index) => ( + setSelectedIndex(index)} + colors={{ + activeContainerColor: '#6200EE', + activeContentColor: '#FFFFFF', + }}> + + {label} + + + ))} + + + ); +} +``` + +## API + +```tsx +import { + SingleChoiceSegmentedButtonRow, + MultiChoiceSegmentedButtonRow, + SegmentedButton, +} from '@expo/ui/jetpack-compose'; +``` + + diff --git a/docs/pages/versions/v56.0.0/sdk/ui/jetpack-compose/shape.mdx b/docs/pages/versions/v56.0.0/sdk/ui/jetpack-compose/shape.mdx new file mode 100644 index 00000000000000..b029ee32a7e56e --- /dev/null +++ b/docs/pages/versions/v56.0.0/sdk/ui/jetpack-compose/shape.mdx @@ -0,0 +1,114 @@ +--- +title: Shape +description: A Jetpack Compose Shape component for drawing geometric shapes. +sourceCodeUrl: 'https://github.com/expo/expo/tree/sdk-56/packages/expo-ui' +packageName: '@expo/ui' +platforms: ['android', 'expo-go'] +--- + +import APISection from '~/components/plugins/APISection'; +import { APIInstallSection } from '~/components/plugins/InstallSection'; + +Expo UI Shape matches the official Jetpack Compose [Shapes](https://developer.android.com/develop/ui/compose/graphics/draw/shapes) API and provides a set of sub-components for drawing geometric shapes such as stars, circles, rectangles, pills, and polygons. + +## Installation + + + +## Usage + +### Basic shapes + +Render common shapes using the `Shape` sub-components. + +```tsx BasicShapesExample.tsx +import { Host, Shape, Row } from '@expo/ui/jetpack-compose'; +import { size } from '@expo/ui/jetpack-compose/modifiers'; + +export default function BasicShapesExample() { + return ( + + + + + + + + + ); +} +``` + +### Shapes with rounded corners + +Use `cornerRounding` and `smoothing` to customize the appearance of shapes. + +```tsx RoundedShapesExample.tsx +import { Host, Shape, Row } from '@expo/ui/jetpack-compose'; +import { size } from '@expo/ui/jetpack-compose/modifiers'; + +export default function RoundedShapesExample() { + return ( + + + + + + + ); +} +``` + +### Polygon and star variants + +Use `verticesCount` and `innerRadius` to control the shape geometry. + +```tsx PolygonShapesExample.tsx +import { Host, Shape, Row } from '@expo/ui/jetpack-compose'; +import { size } from '@expo/ui/jetpack-compose/modifiers'; + +export default function PolygonShapesExample() { + return ( + + + + + + + + ); +} +``` + +## API + +```tsx +import { Shape } from '@expo/ui/jetpack-compose'; +``` + + diff --git a/docs/pages/versions/v56.0.0/sdk/ui/jetpack-compose/slider.mdx b/docs/pages/versions/v56.0.0/sdk/ui/jetpack-compose/slider.mdx new file mode 100644 index 00000000000000..ea87c3449218a2 --- /dev/null +++ b/docs/pages/versions/v56.0.0/sdk/ui/jetpack-compose/slider.mdx @@ -0,0 +1,154 @@ +--- +title: Slider +description: A Jetpack Compose Slider component for selecting values from a range. +sourceCodeUrl: 'https://github.com/expo/expo/tree/sdk-56/packages/expo-ui' +packageName: '@expo/ui' +platforms: ['android', 'expo-go'] +--- + +import APISection from '~/components/plugins/APISection'; +import { APIInstallSection } from '~/components/plugins/InstallSection'; + +Expo UI Slider matches the official Jetpack Compose [Slider API](https://developer.android.com/develop/ui/compose/components/slider) and allows selecting values from a bounded range. + +## Installation + + + +## Usage + +### Basic slider + +```tsx BasicSliderExample.tsx +import { useState } from 'react'; +import { Host, Slider } from '@expo/ui/jetpack-compose'; + +export default function BasicSliderExample() { + const [value, setValue] = useState(0.5); + + return ( + + + + ); +} +``` + +### Slider with custom range + +Use the `min` and `max` props to define the slider's value range. + +```tsx CustomRangeSliderExample.tsx +import { useState } from 'react'; +import { Host, Slider } from '@expo/ui/jetpack-compose'; + +export default function CustomRangeSliderExample() { + const [value, setValue] = useState(50); + + return ( + + + + ); +} +``` + +### Slider with steps + +Use the `steps` prop to define discrete increments. Set `steps` to `0` for continuous values. + +```tsx SteppedSliderExample.tsx +import { useState } from 'react'; +import { Host, Slider } from '@expo/ui/jetpack-compose'; + +export default function SteppedSliderExample() { + const [value, setValue] = useState(0); + + return ( + + + + ); +} +``` + +### Custom colors + +Use the `colors` prop to override the default Material3 colors for the slider's thumb, track, and tick marks. + +```tsx CustomColorsSliderExample.tsx +import { useState } from 'react'; +import { Host, Slider } from '@expo/ui/jetpack-compose'; + +export default function CustomColorsSliderExample() { + const [value, setValue] = useState(0.5); + + return ( + + + + ); +} +``` + +### Custom thumb and track + +Use both `Slider.Thumb` and `Slider.Track` slots for a fully custom slider appearance. + +```tsx FullyCustomSliderExample.tsx +import { useState } from 'react'; +import { Host, Slider, Shape, Row, Box } from '@expo/ui/jetpack-compose'; +import { + fillMaxWidth, + height, + weight, + size, + clip, + background, + Shapes, +} from '@expo/ui/jetpack-compose/modifiers'; + +export default function FullyCustomSliderExample() { + const [value, setValue] = useState(0.5); + + return ( + + + + + + + + + + + + + + ); +} +``` + +## API + +```tsx +import { Slider } from '@expo/ui/jetpack-compose'; +``` + + diff --git a/docs/pages/versions/v56.0.0/sdk/ui/jetpack-compose/spacer.mdx b/docs/pages/versions/v56.0.0/sdk/ui/jetpack-compose/spacer.mdx new file mode 100644 index 00000000000000..204685f4ed92e2 --- /dev/null +++ b/docs/pages/versions/v56.0.0/sdk/ui/jetpack-compose/spacer.mdx @@ -0,0 +1,68 @@ +--- +title: Spacer +description: A Jetpack Compose Spacer component for adding flexible space between elements. +sourceCodeUrl: 'https://github.com/expo/expo/tree/sdk-56/packages/expo-ui' +packageName: '@expo/ui' +platforms: ['android', 'expo-go'] +--- + +import APISection from '~/components/plugins/APISection'; +import { APIInstallSection } from '~/components/plugins/InstallSection'; + +Expo UI Spacer matches the official Jetpack Compose [Spacer](https://developer.android.com/reference/kotlin/androidx/compose/foundation/layout/package-summary#Spacer) API and is used to add flexible or fixed-size space between elements in a layout. + +## Installation + + + +## Usage + +### Spacer with weight + +Use the `weight()` modifier to make the spacer fill available space proportionally within a `Row` or `Column`. + +```tsx SpacerWeightExample.tsx +import { Host, Row, Spacer, Text } from '@expo/ui/jetpack-compose'; +import { fillMaxWidth, weight } from '@expo/ui/jetpack-compose/modifiers'; + +export default function SpacerWeightExample() { + return ( + + + Left + + Right + + + ); +} +``` + +### Spacer with fixed size + +Use a `height` or `width` modifier to create a spacer with a fixed dimension. + +```tsx SpacerFixedSizeExample.tsx +import { Host, Column, Spacer, Text } from '@expo/ui/jetpack-compose'; +import { height } from '@expo/ui/jetpack-compose/modifiers'; + +export default function SpacerFixedSizeExample() { + return ( + + + Above + + Below (24dp gap) + + + ); +} +``` + +## API + +```tsx +import { Spacer } from '@expo/ui/jetpack-compose'; +``` + + diff --git a/docs/pages/versions/v56.0.0/sdk/ui/jetpack-compose/surface.mdx b/docs/pages/versions/v56.0.0/sdk/ui/jetpack-compose/surface.mdx new file mode 100644 index 00000000000000..3662e0a4eb5b7c --- /dev/null +++ b/docs/pages/versions/v56.0.0/sdk/ui/jetpack-compose/surface.mdx @@ -0,0 +1,114 @@ +--- +title: Surface +description: A Jetpack Compose Surface component for styled content containers. +sourceCodeUrl: 'https://github.com/expo/expo/tree/sdk-56/packages/expo-ui' +packageName: '@expo/ui' +platforms: ['android', 'expo-go'] +--- + +import APISection from '~/components/plugins/APISection'; +import { APIInstallSection } from '~/components/plugins/InstallSection'; + +Expo UI Surface matches the official Jetpack Compose [Surface](https://developer.android.com/develop/ui/compose/designsystems/material3) API and provides a container that applies Material Design surface styling including color, elevation, and content color. + +## Installation + + + +## Usage + +### Basic surface + +```tsx BasicSurfaceExample.tsx +import { Host, Surface, Text } from '@expo/ui/jetpack-compose'; +import { paddingAll } from '@expo/ui/jetpack-compose/modifiers'; + +export default function BasicSurfaceExample() { + return ( + + + Content on a surface + + + ); +} +``` + +### Surface with elevation + +Use `tonalElevation` and `shadowElevation` to control the visual depth of the surface. + +```tsx SurfaceElevationExample.tsx +import { Host, Surface, Column, Text } from '@expo/ui/jetpack-compose'; +import { paddingAll } from '@expo/ui/jetpack-compose/modifiers'; + +export default function SurfaceElevationExample() { + return ( + + + + Low elevation + + + High elevation + + + + ); +} +``` + +### Surface with custom colors + +Use the `color` and `contentColor` props to override the default Material theme colors. + +```tsx SurfaceCustomColorsExample.tsx +import { Host, Surface, Text } from '@expo/ui/jetpack-compose'; +import { paddingAll } from '@expo/ui/jetpack-compose/modifiers'; + +export default function SurfaceCustomColorsExample() { + return ( + + + Custom colored surface + + + ); +} +``` + +### Surface with shape and border + +Use the `shape` prop to clip content and `border` to draw a stroke around the surface. + +```tsx SurfaceShapeBorderExample.tsx +import { Host, Surface, Shape, Text } from '@expo/ui/jetpack-compose'; +import { paddingAll } from '@expo/ui/jetpack-compose/modifiers'; + +export default function SurfaceShapeBorderExample() { + return ( + + + Rounded surface with border + + + ); +} +``` + +## API + +```tsx +import { Surface } from '@expo/ui/jetpack-compose'; +``` + + diff --git a/docs/pages/versions/v56.0.0/sdk/ui/jetpack-compose/switch.mdx b/docs/pages/versions/v56.0.0/sdk/ui/jetpack-compose/switch.mdx new file mode 100644 index 00000000000000..e266297d748c54 --- /dev/null +++ b/docs/pages/versions/v56.0.0/sdk/ui/jetpack-compose/switch.mdx @@ -0,0 +1,111 @@ +--- +title: Switch +description: A Jetpack Compose Switch component for toggle controls. +sourceCodeUrl: 'https://github.com/expo/expo/tree/sdk-56/packages/expo-ui' +packageName: '@expo/ui' +platforms: ['android', 'expo-go'] +--- + +import APISection from '~/components/plugins/APISection'; +import { APIInstallSection } from '~/components/plugins/InstallSection'; + +Expo UI Switch matches the official Jetpack Compose [Switch](https://developer.android.com/develop/ui/compose/components/switch) API. + +## Installation + + + +## Usage + +### Toggle switch + +```tsx ToggleSwitchExample.tsx +import { useState } from 'react'; +import { Host, Switch } from '@expo/ui/jetpack-compose'; + +export default function ToggleSwitchExample() { + const [checked, setChecked] = useState(false); + + return ( + + + + ); +} +``` + +### Custom colors + +```tsx CustomColorsExample.tsx +import { useState } from 'react'; +import { Host, Switch } from '@expo/ui/jetpack-compose'; + +export default function CustomColorsExample() { + const [checked, setChecked] = useState(false); + + return ( + + + + ); +} +``` + +### Custom thumb content + +Use `Switch.ThumbContent` to render a custom element inside the thumb. `Switch.DefaultIconSize` gives you the M3 default icon size so your content fits perfectly. + +```tsx ThumbContentExample.tsx +import { useState } from 'react'; +import { Host, Switch, Box } from '@expo/ui/jetpack-compose'; +import { size, clip, background, Shapes } from '@expo/ui/jetpack-compose/modifiers'; + +export default function ThumbContentExample() { + const [checked, setChecked] = useState(false); + + return ( + + + + + + + + ); +} +``` + +## API + +```tsx +import { Switch } from '@expo/ui/jetpack-compose'; +``` + + diff --git a/docs/pages/versions/v56.0.0/sdk/ui/jetpack-compose/text.mdx b/docs/pages/versions/v56.0.0/sdk/ui/jetpack-compose/text.mdx new file mode 100644 index 00000000000000..c5452697c0542c --- /dev/null +++ b/docs/pages/versions/v56.0.0/sdk/ui/jetpack-compose/text.mdx @@ -0,0 +1,174 @@ +--- +title: Text +description: A Jetpack Compose Text component for displaying styled text. +sourceCodeUrl: 'https://github.com/expo/expo/tree/sdk-56/packages/expo-ui' +packageName: '@expo/ui' +platforms: ['android', 'expo-go'] +--- + +import APISection from '~/components/plugins/APISection'; +import { APIInstallSection } from '~/components/plugins/InstallSection'; + +Expo UI Text matches the official Jetpack Compose [Text styling](https://developer.android.com/develop/ui/compose/text/style-text) API and displays text with Material 3 typography styles, custom fonts, and text formatting options. + +## Installation + + + +## Usage + +### Basic text + +```tsx BasicTextExample.tsx +import { Host, Text } from '@expo/ui/jetpack-compose'; + +export default function BasicTextExample() { + return ( + + Hello, world! + + ); +} +``` + +### Typography styles + +Use the `style` prop with `typography` to apply Material 3 typography presets. + +```tsx TypographyExample.tsx +import { Host, Text, Column } from '@expo/ui/jetpack-compose'; +import { paddingAll } from '@expo/ui/jetpack-compose/modifiers'; + +export default function TypographyExample() { + return ( + + + Display Large + Headline Medium + Body Small + Label Large + + + ); +} +``` + +### Text with maxLines and overflow + +Control text truncation with `maxLines` and `overflow`. + +```tsx TextOverflowExample.tsx +import { Host, Text } from '@expo/ui/jetpack-compose'; +import { width } from '@expo/ui/jetpack-compose/modifiers'; + +export default function TextOverflowExample() { + return ( + + + This is a long paragraph of text that will be truncated after two lines with an ellipsis at + the end to indicate there is more content. + + + ); +} +``` + +### Styled text + +Apply custom text styles including font weight, style, size, and decoration. + +```tsx StyledTextExample.tsx +import { Host, Text, Column } from '@expo/ui/jetpack-compose'; +import { paddingAll } from '@expo/ui/jetpack-compose/modifiers'; + +export default function StyledTextExample() { + return ( + + + Bold text + Italic text + Underlined text + Spaced out text + + Colored and centered + + + + ); +} +``` + +### Nested text + +Nest `` components to apply inline styles to parts of a sentence. Child spans inherit styles from their parent. For example, a bold parent with an italic child renders the child as bold and italic. + +```tsx NestedTextExample.tsx +import { Host, Text, Column } from '@expo/ui/jetpack-compose'; +import { paddingAll } from '@expo/ui/jetpack-compose/modifiers'; + +export default function NestedTextExample() { + return ( + + + {/* Bold parent, italic child inherits bold */} + + Hello world! + + + {/* Mixed inline styles */} + + Normal, italic,{' '} + bold, and{' '} + underlined + + + {/* Color and background overrides per span */} + + Click{' '} + + here + {' '} + or highlighted + + + {/* Deep nesting — styles accumulate */} + + Bold{' '} + + bold+italic bold+italic+underline + + + + + ); +} +``` + +### Custom fonts + +Use fonts loaded via [`expo-font`](/versions/latest/sdk/font/) by passing the font family name to `style.fontFamily`. + +```tsx CustomFontExample.tsx +import { Host, Text, Column } from '@expo/ui/jetpack-compose'; +import { paddingAll } from '@expo/ui/jetpack-compose/modifiers'; + +export default function CustomFontExample() { + return ( + + + System serif font + System monospace font + Custom Inter Bold font + + + ); +} +``` + +## API + +```tsx +import { Text } from '@expo/ui/jetpack-compose'; +``` + + diff --git a/docs/pages/versions/v56.0.0/sdk/ui/jetpack-compose/textfield.mdx b/docs/pages/versions/v56.0.0/sdk/ui/jetpack-compose/textfield.mdx new file mode 100644 index 00000000000000..d6f322eb9ca396 --- /dev/null +++ b/docs/pages/versions/v56.0.0/sdk/ui/jetpack-compose/textfield.mdx @@ -0,0 +1,296 @@ +--- +title: TextField +description: Jetpack Compose TextField components for native Material3 text input. +sourceCodeUrl: 'https://github.com/expo/expo/tree/sdk-56/packages/expo-ui' +packageName: '@expo/ui' +platforms: ['android', 'expo-go'] +--- + +import APISection from '~/components/plugins/APISection'; +import { APIInstallSection } from '~/components/plugins/InstallSection'; + +Expo UI provides two text field components that match the official Jetpack Compose [TextField API](https://developer.android.com/develop/ui/compose/text/user-input): `TextField` (filled) and `OutlinedTextField` (outlined border). Both variants share the same props and support composable slot children for label, placeholder, icons, prefix, suffix, and supporting text. + +| Type | Appearance | Purpose | +| -------- | ---------------------------------------------- | ------------------------------------------------------------------------------------------------------------- | +| Filled | Solid background with a bottom indicator line. | Default text input style following Material3 design. Use for most forms and input fields. | +| Outlined | Transparent background with a border outline. | Alternative style that provides a distinct visual boundary. Use when filled fields blend into the background. | + +## Installation + + + +## Usage + +### Basic text field + +A filled text field is the default Material3 text input style. + +```tsx BasicTextFieldExample.tsx +import { useState } from 'react'; +import { Host, TextField, Text } from '@expo/ui/jetpack-compose'; + +export default function BasicTextFieldExample() { + const [value, setValue] = useState(''); + + return ( + + + + Username + + + + ); +} +``` + +### Outlined text field + +Use `OutlinedTextField` for a text field with a border outline instead of a filled background. + +```tsx OutlinedTextFieldExample.tsx +import { useState } from 'react'; +import { Host, OutlinedTextField, Text } from '@expo/ui/jetpack-compose'; + +export default function OutlinedTextFieldExample() { + const [value, setValue] = useState(''); + + return ( + + + + Email + + + you@example.com + + + + ); +} +``` + +### Slots + +Both `TextField` and `OutlinedTextField` support 7 composable slots that match the Compose API: `Label`, `Placeholder`, `LeadingIcon`, `TrailingIcon`, `Prefix`, `Suffix`, and `SupportingText`. + +```tsx TextFieldSlotsExample.tsx +import { useState } from 'react'; +import { Host, TextField, Text } from '@expo/ui/jetpack-compose'; + +export default function TextFieldSlotsExample() { + const [value, setValue] = useState(''); + + return ( + + + + Price + + + 0.00 + + + 💰 + + + $ + + + USD + + + Enter the amount + + + + ); +} +``` + +### Keyboard options + +Use the `keyboardOptions` prop to configure the keyboard type, capitalization, auto-correct, and IME action. + +```tsx KeyboardOptionsExample.tsx +import { useState } from 'react'; +import { Host, TextField, Text } from '@expo/ui/jetpack-compose'; + +export default function KeyboardOptionsExample() { + const [value, setValue] = useState(''); + + return ( + + + + Email + + + + ); +} +``` + +### Keyboard actions + +Use the `keyboardActions` prop to handle IME action button presses. The triggered callback depends on the `imeAction` set in `keyboardOptions`. Each callback receives the current text value. + +```tsx KeyboardActionsExample.tsx +import { useState } from 'react'; +import { Host, TextField, Text } from '@expo/ui/jetpack-compose'; + +export default function KeyboardActionsExample() { + const [value, setValue] = useState(''); + const [submitted, setSubmitted] = useState(''); + + return ( + + setSubmitted(text), + }}> + + Search + + + + ); +} +``` + +### Error state + +Set `isError` to display the text field in an error state. Combine with `SupportingText` to show an error message. + +```tsx ErrorStateExample.tsx +import { useState } from 'react'; +import { Host, OutlinedTextField, Text } from '@expo/ui/jetpack-compose'; + +export default function ErrorStateExample() { + const [value, setValue] = useState(''); + const hasError = value.length > 0 && !value.includes('@'); + + return ( + + + + Email + + + {hasError ? 'Please enter a valid email' : 'Required'} + + + + ); +} +``` + +### Imperative ref + +Use a ref to imperatively set text, clear the field, change the selection, or move focus. + +```tsx ImperativeRefExample.tsx +import { useRef, useState } from 'react'; +import { Host, TextField, TextFieldRef, Button, Row, Text, Column } from '@expo/ui/jetpack-compose'; +import { padding } from '@expo/ui/jetpack-compose/modifiers'; + +export default function ImperativeRefExample() { + const ref = useRef(null); + const [value, setValue] = useState(''); + + return ( + + + + + Name + + + + + + + + + + + + + + ); +} +``` + +### Worklet text masking + +When `onValueChange` is marked with the `'worklet'` directive, it runs synchronously on the UI thread, so writes to [`useNativeState`](usenativestate) observables inside the callback take effect before the next frame. There is no flicker between the typed text and the masked text. The example below masks a phone number as the user types and writes both `value` and `selection` from the worklet to keep the cursor at the end of the formatted value. + +> **Note:** Worklets require installing [`react-native-reanimated`](https://docs.swmansion.com/react-native-reanimated/) and [`react-native-worklets`](https://docs.swmansion.com/react-native-worklets/). + +```tsx WorkletPhoneMaskExample.tsx +import { Host, TextField, Text, useNativeState } from '@expo/ui/jetpack-compose'; +import { fillMaxWidth } from '@expo/ui/jetpack-compose/modifiers'; + +export default function WorkletPhoneMaskExample() { + const phone = useNativeState(''); + const selection = useNativeState({ start: 0, end: 0 }); + + return ( + + { + 'worklet'; + const digits = v.replace(/\D/g, '').slice(0, 10); + let formatted = digits; + if (digits.length > 6) { + formatted = `(${digits.slice(0, 3)}) ${digits.slice(3, 6)}-${digits.slice(6)}`; + } else if (digits.length > 3) { + formatted = `(${digits.slice(0, 3)}) ${digits.slice(3)}`; + } + if (formatted !== v) { + phone.value = formatted; + selection.value = { start: formatted.length, end: formatted.length }; + } + }}> + + (555) 123-4567 + + + + ); +} +``` + +## API + +```tsx +import { TextField, OutlinedTextField } from '@expo/ui/jetpack-compose'; +``` + + diff --git a/docs/pages/versions/v56.0.0/sdk/ui/jetpack-compose/togglebutton.mdx b/docs/pages/versions/v56.0.0/sdk/ui/jetpack-compose/togglebutton.mdx new file mode 100644 index 00000000000000..80c3858db08407 --- /dev/null +++ b/docs/pages/versions/v56.0.0/sdk/ui/jetpack-compose/togglebutton.mdx @@ -0,0 +1,137 @@ +--- +title: ToggleButton +description: Jetpack Compose ToggleButton components for displaying native Material3 toggle buttons. +sourceCodeUrl: 'https://github.com/expo/expo/tree/sdk-56/packages/expo-ui' +packageName: '@expo/ui' +platforms: ['android', 'expo-go'] +--- + +import APISection from '~/components/plugins/APISection'; +import { APIInstallSection } from '~/components/plugins/InstallSection'; + +Expo UI provides four toggle button components that match the official Jetpack Compose Toggle Button API: [`ToggleButton`](https://kotlinlang.org/api/compose-multiplatform/material3/androidx.compose.material3/-toggle-button.html), [`IconToggleButton`](https://kotlinlang.org/api/compose-multiplatform/material3/androidx.compose.material3/-icon-toggle-button.html), [`FilledIconToggleButton`](https://kotlinlang.org/api/compose-multiplatform/material3/androidx.compose.material3/-filled-icon-toggle-button.html), and [`OutlinedIconToggleButton`](https://kotlinlang.org/api/compose-multiplatform/material3/androidx.compose.material3/-outlined-icon-toggle-button.html). + +## Installation + + + +## Usage + +### Basic toggle button + +A toggle button with text and icon content. + +```tsx BasicToggleButtonExample.tsx +import { useState } from 'react'; +import { Host, ToggleButton, Text } from '@expo/ui/jetpack-compose'; + +export default function BasicToggleButtonExample() { + const [checked, setChecked] = useState(false); + + return ( + + + Favorite + + + ); +} +``` + +### Icon toggle button variants + +Use different icon toggle button components to convey varying levels of emphasis. + +```tsx IconToggleButtonVariantsExample.tsx +import { useState } from 'react'; +import { + Host, + IconToggleButton, + FilledIconToggleButton, + OutlinedIconToggleButton, + Icon, + Row, +} from '@expo/ui/jetpack-compose'; + +const starIcon = require('./assets/star.png'); + +export default function IconToggleButtonVariantsExample() { + const [checked1, setChecked1] = useState(false); + const [checked2, setChecked2] = useState(true); + const [checked3, setChecked3] = useState(false); + + return ( + + + + + + + + + + + + + + ); +} +``` + +### Custom colors + +Override checked and unchecked colors using the `colors` prop. + +```tsx CustomColorsToggleButtonExample.tsx +import { useState } from 'react'; +import { Host, ToggleButton, Text } from '@expo/ui/jetpack-compose'; + +export default function CustomColorsToggleButtonExample() { + const [checked, setChecked] = useState(true); + + return ( + + + {checked ? 'ON' : 'OFF'} + + + ); +} +``` + +### Disabled toggle button + +```tsx DisabledToggleButtonExample.tsx +import { Host, ToggleButton, Text } from '@expo/ui/jetpack-compose'; + +export default function DisabledToggleButtonExample() { + return ( + + + Disabled + + + ); +} +``` + +## API + +```tsx +import { + ToggleButton, + IconToggleButton, + FilledIconToggleButton, + OutlinedIconToggleButton, +} from '@expo/ui/jetpack-compose'; +``` + + diff --git a/docs/pages/versions/v56.0.0/sdk/ui/jetpack-compose/tooltip.mdx b/docs/pages/versions/v56.0.0/sdk/ui/jetpack-compose/tooltip.mdx new file mode 100644 index 00000000000000..ec97f661981311 --- /dev/null +++ b/docs/pages/versions/v56.0.0/sdk/ui/jetpack-compose/tooltip.mdx @@ -0,0 +1,144 @@ +--- +title: Tooltip +description: Jetpack Compose Tooltip components for displaying contextual information on long-press. +sourceCodeUrl: 'https://github.com/expo/expo/tree/sdk-56/packages/expo-ui' +packageName: '@expo/ui' +platforms: ['android', 'expo-go'] +--- + +import APISection from '~/components/plugins/APISection'; +import { APIInstallSection } from '~/components/plugins/InstallSection'; + +Expo UI Tooltip matches the official Jetpack Compose [Tooltip](https://developer.android.com/develop/ui/compose/components/tooltip) API. `TooltipBox` wraps anchor content and displays a tooltip. The tooltip content is provided via the `TooltipBox.PlainTooltip` or `TooltipBox.RichTooltip` compound components, which match [`PlainTooltip`](https://developer.android.com/develop/ui/compose/components/tooltip#display-plain) and [`RichTooltip`](https://developer.android.com/develop/ui/compose/components/tooltip#display-rich) respectively. Tooltips can be triggered by long-press or shown programmatically via `ref`. + +## Installation + + + +## Usage + +### Plain tooltip + +Long-press the anchor content to display the tooltip. + +```tsx PlainTooltipExample.tsx +import { Host, TooltipBox, Button, Text } from '@expo/ui/jetpack-compose'; + +export default function PlainTooltipExample() { + return ( + + + + Add to favorites + + + + + ); +} +``` + +### Rich tooltip with title and body + +Use `TooltipBox.RichTooltip` with `Title` and `Text` compound component pattern for more detailed contextual information. + +```tsx RichTooltipExample.tsx +import { Host, TooltipBox, Button, Text } from '@expo/ui/jetpack-compose'; + +export default function RichTooltipExample() { + return ( + + + + + Camera + + + Take photos and record videos with your device camera. + + + + + + ); +} +``` + +### Rich tooltip with action + +Add an interactive action with `TooltipBox.RichTooltip.Action`. Use `isPersistent` so the tooltip stays visible for the user to tap it. `hasAction` is automatically derived when an action slot is present. + +```tsx RichTooltipActionExample.tsx +import { Host, TooltipBox, Button, TextButton, Text } from '@expo/ui/jetpack-compose'; + +export default function RichTooltipActionExample() { + return ( + + + + + Permissions Required + + + This feature requires camera and microphone access. + + + {}}> + Learn More + + + + + + + ); +} +``` + +### Programmatic show and dismiss + +Use a `ref` to imperatively `show()` or `dismiss()` the tooltip without requiring a long-press. + +```tsx ProgrammaticTooltipExample.tsx +import { useRef } from 'react'; +import { Host, TooltipBox, type TooltipBoxRef, Button, Text, Row } from '@expo/ui/jetpack-compose'; + +export default function ProgrammaticTooltipExample() { + const tooltipRef = useRef(null); + + return ( + + + + Shown programmatically! + + + + + + + + + ); +} +``` + +## API + +```tsx +import { TooltipBox } from '@expo/ui/jetpack-compose'; +``` + + diff --git a/docs/pages/versions/v56.0.0/sdk/ui/jetpack-compose/usenativestate.mdx b/docs/pages/versions/v56.0.0/sdk/ui/jetpack-compose/usenativestate.mdx new file mode 100644 index 00000000000000..ce2eb7b743d6d7 --- /dev/null +++ b/docs/pages/versions/v56.0.0/sdk/ui/jetpack-compose/usenativestate.mdx @@ -0,0 +1,72 @@ +--- +title: useNativeState +description: A React hook that creates observable state shared between JavaScript and native Jetpack Compose views. +sourceCodeUrl: 'https://github.com/expo/expo/tree/sdk-56/packages/expo-ui/src/State/useNativeState.ts' +packageName: '@expo/ui' +platforms: ['android', 'expo-go'] +--- + +import APISection from '~/components/plugins/APISection'; +import { APIInstallSection } from '~/components/plugins/InstallSection'; + +`useNativeState` returns an [`ObservableState`](#observablestate) that maps to a Compose [`MutableState`](https://developer.android.com/reference/kotlin/androidx/compose/runtime/MutableState) on the native side, so reads and writes to `.value` are tracked directly by Compose without going through the React render cycle. This lets you update the native view synchronously from a worklet on the UI thread. + +## Installation + + + +## Usage + +> **Note:** Using worklets requires installing [`react-native-reanimated`](https://docs.swmansion.com/react-native-reanimated/) and [`react-native-worklets`](https://docs.swmansion.com/react-native-worklets/) in your project. `useNativeState` itself works without them, but the synchronous UI-thread updates shown below depend on the worklet runtime. + +The example below masks a phone number as the user types. The formatting and the writes to `maskedPhone.value` (text) and `selection.value` (cursor position) all happen synchronously on the UI thread, so there is no flicker between the typed value and the masked value. + +```tsx WorkletPhoneMaskExample.tsx +import { Host, TextField, Text as ComposeText, useNativeState } from '@expo/ui/jetpack-compose'; +import { fillMaxWidth } from '@expo/ui/jetpack-compose/modifiers'; + +export default function WorkletPhoneMaskExample() { + const maskedPhone = useNativeState(''); + const selection = useNativeState({ start: 0, end: 0 }); + + return ( + + { + 'worklet'; + const digits = v.replace(/\D/g, '').slice(0, 10); + let formatted: string; + if (digits.length === 0) { + formatted = ''; + } else if (digits.length <= 3) { + formatted = digits; + } else if (digits.length <= 6) { + formatted = `(${digits.slice(0, 3)}) ${digits.slice(3)}`; + } else { + formatted = `(${digits.slice(0, 3)}) ${digits.slice(3, 6)}-${digits.slice(6)}`; + } + if (formatted !== v) { + maskedPhone.value = formatted; + selection.value = { start: formatted.length, end: formatted.length }; + } + }}> + + (555) 123-4567 + + + + ); +} +``` + +## API + +```tsx +import { useNativeState } from '@expo/ui/jetpack-compose'; +``` + + diff --git a/docs/pages/versions/v56.0.0/sdk/ui/swift-ui/accessorywidgetbackground.mdx b/docs/pages/versions/v56.0.0/sdk/ui/swift-ui/accessorywidgetbackground.mdx new file mode 100644 index 00000000000000..4d694c1d342118 --- /dev/null +++ b/docs/pages/versions/v56.0.0/sdk/ui/swift-ui/accessorywidgetbackground.mdx @@ -0,0 +1,46 @@ +--- +title: AccessoryWidgetBackground +description: A SwiftUI adaptive background view that provides a standard appearance based on the the widget's environment. +sourceCodeUrl: 'https://github.com/expo/expo/tree/sdk-56/packages/expo-ui' +packageName: '@expo/ui' +platforms: ['ios', 'expo-go'] +--- + +import APISection from '~/components/plugins/APISection'; +import { APIInstallSection } from '~/components/plugins/InstallSection'; + +Expo UI AccessoryWidgetBackground matches the official SwiftUI [AccessoryWidgetBackground API](https://developer.apple.com/documentation/widgetkit/accessorywidgetbackground) and creates an adaptive background view that provides a standard appearance based on the the widget's environment. + +## Installation + + + +## Usage + +### Basic accessory widget background + +```tsx BasicAccessoryWidgetBackground.tsx +import { AccessoryWidgetBackground, VStack, Text, ZStack } from '@expo/ui/swift-ui'; + +export default function BasicAccessoryWidgetBackground() { + return ( + + + + MON + + + ); +} +``` + +## API + +```tsx +import { AccessoryWidgetBackground } from '@expo/ui/swift-ui'; +``` + + diff --git a/docs/pages/versions/v56.0.0/sdk/ui/swift-ui/bottomsheet.mdx b/docs/pages/versions/v56.0.0/sdk/ui/swift-ui/bottomsheet.mdx new file mode 100644 index 00000000000000..1311a380fe95a6 --- /dev/null +++ b/docs/pages/versions/v56.0.0/sdk/ui/swift-ui/bottomsheet.mdx @@ -0,0 +1,331 @@ +--- +title: BottomSheet +description: A SwiftUI BottomSheet component that presents content from the bottom of the screen. +sourceCodeUrl: 'https://github.com/expo/expo/tree/sdk-56/packages/expo-ui' +packageName: '@expo/ui' +platforms: ['ios', 'tvos', 'expo-go'] +--- + +import APISection from '~/components/plugins/APISection'; +import { APIInstallSection } from '~/components/plugins/InstallSection'; + +Expo UI BottomSheet matches the official SwiftUI [sheet API]() and presents content from the bottom of the screen. + +## Installation + + + +## Usage + +### Basic bottom sheet + +```tsx BasicBottomSheetExample.tsx +import { useState } from 'react'; +import { Host, BottomSheet, Button, Text, VStack } from '@expo/ui/swift-ui'; + +export default function BasicBottomSheetExample() { + const [isPresented, setIsPresented] = useState(false); + + return ( + + + + + ); +} +``` + +## API + +```tsx +import { Button } from '@expo/ui/swift-ui'; +``` + + diff --git a/docs/pages/versions/v56.0.0/sdk/ui/swift-ui/colorpicker.mdx b/docs/pages/versions/v56.0.0/sdk/ui/swift-ui/colorpicker.mdx new file mode 100644 index 00000000000000..ed07ba9d115751 --- /dev/null +++ b/docs/pages/versions/v56.0.0/sdk/ui/swift-ui/colorpicker.mdx @@ -0,0 +1,67 @@ +--- +title: ColorPicker +description: A SwiftUI ColorPicker component for selecting colors. +sourceCodeUrl: 'https://github.com/expo/expo/tree/sdk-56/packages/expo-ui' +packageName: '@expo/ui' +platforms: ['ios', 'expo-go'] +--- + +import APISection from '~/components/plugins/APISection'; +import { APIInstallSection } from '~/components/plugins/InstallSection'; + +Expo UI ColorPicker matches the official SwiftUI [ColorPicker API](https://developer.apple.com/documentation/swiftui/colorpicker) and allows app users to select colors from a palette. + +## Installation + + + +## Usage + +### Basic color picker + +```tsx ColorPickerExample.tsx +import { useState } from 'react'; +import { Host, ColorPicker } from '@expo/ui/swift-ui'; + +export default function ColorPickerExample() { + const [color, setColor] = useState('#FF6347'); + + return ( + + + + ); +} +``` + +### Color picker with opacity support + +Use the `supportsOpacity` prop to allow users to select colors with alpha transparency. + +```tsx ColorPickerOpacityExample.tsx +import { useState } from 'react'; +import { Host, ColorPicker } from '@expo/ui/swift-ui'; + +export default function ColorPickerOpacityExample() { + const [color, setColor] = useState('#FF634780'); + + return ( + + + + ); +} +``` + +## API + +```tsx +import { ColorPicker } from '@expo/ui/swift-ui'; +``` + + diff --git a/docs/pages/versions/v56.0.0/sdk/ui/swift-ui/confirmationdialog.mdx b/docs/pages/versions/v56.0.0/sdk/ui/swift-ui/confirmationdialog.mdx new file mode 100644 index 00000000000000..842030ba67f692 --- /dev/null +++ b/docs/pages/versions/v56.0.0/sdk/ui/swift-ui/confirmationdialog.mdx @@ -0,0 +1,167 @@ +--- +title: ConfirmationDialog +description: A SwiftUI ConfirmationDialog component for presenting confirmation prompts. +sourceCodeUrl: 'https://github.com/expo/expo/tree/sdk-56/packages/expo-ui' +packageName: '@expo/ui' +platforms: ['ios', 'tvos', 'expo-go'] +--- + +import APISection from '~/components/plugins/APISection'; +import { APIInstallSection } from '~/components/plugins/InstallSection'; + +Expo UI ConfirmationDialog matches the official SwiftUI [confirmationDialog API]() and presents an action sheet-style dialog with a title, actions, and an optional message. + +## Installation + + + +## Usage + +### Basic confirmation dialog + +Use `ConfirmationDialog.Trigger` to define the visible element and `ConfirmationDialog.Actions` to provide the dialog buttons. + +```tsx BasicConfirmationDialogExample.tsx +import { useState } from 'react'; +import { Host, ConfirmationDialog, Button, Text } from '@expo/ui/swift-ui'; + +export default function BasicConfirmationDialogExample() { + const [isPresented, setIsPresented] = useState(false); + + return ( + + + + + + + + ); +} +``` + +## API + +```tsx +import { Namespace } from '@expo/ui/swift-ui'; +``` + + diff --git a/docs/pages/versions/v56.0.0/sdk/ui/swift-ui/overlay.mdx b/docs/pages/versions/v56.0.0/sdk/ui/swift-ui/overlay.mdx new file mode 100644 index 00000000000000..d7880f569c687e --- /dev/null +++ b/docs/pages/versions/v56.0.0/sdk/ui/swift-ui/overlay.mdx @@ -0,0 +1,64 @@ +--- +title: Overlay +description: A SwiftUI Overlay component for layering content on top of another view. +sourceCodeUrl: 'https://github.com/expo/expo/tree/sdk-56/packages/expo-ui' +packageName: '@expo/ui' +platforms: ['ios', 'tvOS', 'expo-go'] +--- + +import APISection from '~/components/plugins/APISection'; +import { APIInstallSection } from '~/components/plugins/InstallSection'; + +Expo UI Overlay matches the official SwiftUI [overlay]() modifier and provides a way to layer secondary content on top of a view, positioned with a specified alignment. + +## Installation + + + +## Usage + +```tsx OverlayExample.tsx +import { Host, Overlay, Text, Image } from '@expo/ui/swift-ui'; +import { + foregroundStyle, + frame, + font, + background, + clipShape, + offset, +} from '@expo/ui/swift-ui/modifiers'; + +export default function OverlayExample() { + return ( + + + + + + 3 + + + + + ); +} +``` + +## API + +```tsx +import { Overlay } from '@expo/ui/swift-ui'; +``` + + diff --git a/docs/pages/versions/v56.0.0/sdk/ui/swift-ui/picker.mdx b/docs/pages/versions/v56.0.0/sdk/ui/swift-ui/picker.mdx new file mode 100644 index 00000000000000..adb8c9424d43a2 --- /dev/null +++ b/docs/pages/versions/v56.0.0/sdk/ui/swift-ui/picker.mdx @@ -0,0 +1,122 @@ +--- +title: Picker +description: A SwiftUI Picker component for selecting options from a list. +sourceCodeUrl: 'https://github.com/expo/expo/tree/sdk-56/packages/expo-ui' +packageName: '@expo/ui' +platforms: ['ios', 'tvos', 'expo-go'] +--- + +import APISection from '~/components/plugins/APISection'; +import { APIInstallSection } from '~/components/plugins/InstallSection'; + +Expo UI Picker matches the official SwiftUI [Picker API](https://developer.apple.com/documentation/swiftui/picker) and supports all picker styles via the [`pickerStyle`](modifiers/#pickerstylestyle) modifier. + +## Installation + + + +## Segmented picker + +```tsx SegmentedPickerExample.tsx +import { useState } from 'react'; +import { Host, Picker, Text } from '@expo/ui/swift-ui'; +import { pickerStyle, tag } from '@expo/ui/swift-ui/modifiers'; + +const options = ['Apple', 'Banana', 'Orange']; + +export default function SegmentedPickerExample() { + const [selectedTag, setSelectedTag] = useState(options[0]); + + return ( + + { + setSelectedTag(selection); + }}> + {options.map(option => ( + + {option} + + ))} + + + ); +} +``` + +## Menu picker + +```tsx MenuPickerExample.tsx +import { useState } from 'react'; +import { Host, Picker, Text } from '@expo/ui/swift-ui'; +import { pickerStyle, tag } from '@expo/ui/swift-ui/modifiers'; + +const options = ['Apple', 'Banana', 'Orange']; + +export default function MenuPickerExample() { + const [selectedTag, setSelectedTag] = useState(options[0]); + + return ( + + { + setSelectedTag(selection); + }}> + {options.map(option => ( + + {option} + + ))} + + + ); +} +``` + +## Wheel picker + +> **Warning** The wheel variant is not available on Apple TV. + +```tsx WheelPickerExample.tsx +import { useState } from 'react'; +import { Host, Picker, Text } from '@expo/ui/swift-ui'; +import { pickerStyle, tag } from '@expo/ui/swift-ui/modifiers'; + +const options = ['Apple', 'Banana', 'Orange']; + +export default function WheelPickerExample() { + const [selectedTag, setSelectedTag] = useState(options[0]); + + return ( + + { + setSelectedTag(selection); + }}> + {options.map(option => ( + + {option} + + ))} + + + ); +} +``` + +## API + +```tsx +import { Picker } from '@expo/ui/swift-ui'; +``` + + diff --git a/docs/pages/versions/v56.0.0/sdk/ui/swift-ui/popover.mdx b/docs/pages/versions/v56.0.0/sdk/ui/swift-ui/popover.mdx new file mode 100644 index 00000000000000..cab78464db8999 --- /dev/null +++ b/docs/pages/versions/v56.0.0/sdk/ui/swift-ui/popover.mdx @@ -0,0 +1,164 @@ +--- +title: Popover +description: A SwiftUI Popover component for displaying content in a floating overlay. +sourceCodeUrl: 'https://github.com/expo/expo/tree/sdk-56/packages/expo-ui' +packageName: '@expo/ui' +platforms: ['ios', 'expo-go'] +--- + +import APISection from '~/components/plugins/APISection'; +import { APIInstallSection } from '~/components/plugins/InstallSection'; + +Expo UI Popover matches the official SwiftUI [Popover API]() and provides a way to present content in a floating overlay anchored to a trigger element. + +## Installation + + + +## Usage + +### Basic popover + +```tsx BasicPopoverExample.tsx +import { useState } from 'react'; +import { Host, Button, Popover, Text, VStack } from '@expo/ui/swift-ui'; +import { padding } from '@expo/ui/swift-ui/modifiers'; + +export default function BasicPopoverExample() { + const [isPresented, setIsPresented] = useState(false); + + return ( + + setIsPresented(isPresented)}> + + + + ); +} +``` + +### Disabled + +```tsx DisabledButtonExample.tsx +import { Host, Button } from '@expo/ui'; + +export default function DisabledButtonExample() { + return ( + +