Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 11 additions & 9 deletions docs/components/DocumentationPage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ import DocumentationHead from '~/components/DocumentationHead';
import DocumentationNestedScrollLayout, {
DocumentationNestedScrollLayoutHandles,
} from '~/components/DocumentationNestedScrollLayout';
import { buildBreadcrumbListSchema } from '~/constants/structured-data';
import { buildBreadcrumbListSchema, buildTechArticleSchema } from '~/constants/structured-data';
import { usePageApiVersion } from '~/providers/page-api-version';
import versions from '~/public/static/constants/versions.json';
import { PageMetadata } from '~/types/common';
Expand Down Expand Up @@ -65,6 +65,14 @@ export default function DocumentationPage({
const sidebarActiveGroup = RoutesUtils.getPageSection(pathname);
const breadcrumbTrail = getBreadcrumbTrail(routes, pathname);
const breadcrumbSchema = buildBreadcrumbListSchema(breadcrumbTrail);
const canonicalUrl =
version !== 'unversioned' && !RoutesUtils.isInternalPath(pathname)
? RoutesUtils.getCanonicalUrl(pathname)
: undefined;
const techArticleSchema =
title && canonicalUrl
? buildTechArticleSchema({ title, description, modificationDate, url: canonicalUrl })
: null;
const sidebarScrollPosition = process?.browser ? window.__sidebarScroll : 0;
const currentPath = router?.asPath ?? '';
const isLatestSdkPage = currentPath.startsWith('/versions/latest/sdk/');
Expand Down Expand Up @@ -315,14 +323,8 @@ export default function DocumentationPage({
isSidebarCollapsed={isNavigationCollapsed}
isChatExpanded={isAskAIExpanded}>
{breadcrumbSchema && <StructuredData id="breadcrumb-list" data={breadcrumbSchema} />}
<DocumentationHead
title={title}
description={description}
canonicalUrl={
version !== 'unversioned' && !RoutesUtils.isInternalPath(pathname)
? RoutesUtils.getCanonicalUrl(pathname)
: undefined
}>
{techArticleSchema && <StructuredData id="tech-article" data={techArticleSchema} />}
<DocumentationHead title={title} description={description} canonicalUrl={canonicalUrl}>
{hideFromSearch !== true && (
<meta
name="docsearch:version"
Expand Down
105 changes: 104 additions & 1 deletion docs/constants/structured-data.test.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,61 @@
import { buildBreadcrumbListSchema } from './structured-data';
import {
buildBreadcrumbListSchema,
buildFAQPageSchema,
buildTechArticleSchema,
websiteSchema,
} from './structured-data';

describe(buildTechArticleSchema, () => {
it('returns TechArticle with all fields', () => {
const result = buildTechArticleSchema({
title: 'Introduction to Expo Router',
description: 'Expo Router is a file-based routing library.',
modificationDate: '2026-01-15',
url: 'https://docs.expo.dev/router/introduction/',
});

expect(result).toEqual({
'@context': 'https://schema.org',
'@type': 'TechArticle',
headline: 'Introduction to Expo Router',
description: 'Expo Router is a file-based routing library.',
dateModified: '2026-01-15',
publisher: websiteSchema.publisher,
url: 'https://docs.expo.dev/router/introduction/',
});
});

it('omits description when not provided', () => {
const result = buildTechArticleSchema({
title: 'Some page',
url: 'https://docs.expo.dev/some-page/',
});

expect(result).not.toHaveProperty('description');
expect(result.headline).toBe('Some page');
expect(result.url).toBe('https://docs.expo.dev/some-page/');
});

it('omits dateModified when modificationDate is not provided', () => {
const result = buildTechArticleSchema({
title: 'Some page',
description: 'A description.',
url: 'https://docs.expo.dev/some-page/',
});

expect(result).not.toHaveProperty('dateModified');
expect(result).toHaveProperty('description', 'A description.');
});

it('uses websiteSchema publisher', () => {
const result = buildTechArticleSchema({
title: 'Any page',
url: 'https://docs.expo.dev/any/',
});

expect(result.publisher).toBe(websiteSchema.publisher);
});
});

describe(buildBreadcrumbListSchema, () => {
it('returns null for empty array', () => {
Expand Down Expand Up @@ -66,3 +123,49 @@ describe(buildBreadcrumbListSchema, () => {
expect(positions).toEqual([1, 2, 3]);
});
});

describe(buildFAQPageSchema, () => {
it('returns null for empty array', () => {
expect(buildFAQPageSchema([])).toBeNull();
});

it('returns valid FAQPage for a single item', () => {
const result = buildFAQPageSchema([
{ question: 'What is Expo?', answer: 'A framework for React Native.' },
]);

expect(result).toEqual({
'@context': 'https://schema.org',
'@type': 'FAQPage',
mainEntity: [
{
'@type': 'Question',
name: 'What is Expo?',
acceptedAnswer: { '@type': 'Answer', text: 'A framework for React Native.' },
},
],
});
});

it('returns valid FAQPage for multiple items', () => {
const result = buildFAQPageSchema([
{ question: 'Q1?', answer: 'A1' },
{ question: 'Q2?', answer: 'A2' },
{ question: 'Q3?', answer: 'A3' },
]);

expect(result?.mainEntity).toHaveLength(3);
expect(result?.mainEntity[1]).toEqual({
'@type': 'Question',
name: 'Q2?',
acceptedAnswer: { '@type': 'Answer', text: 'A2' },
});
});

it('preserves answer text as-is', () => {
const answer = 'Use `npx expo install` to add packages.\nSee https://docs.expo.dev for more.';
const result = buildFAQPageSchema([{ question: 'How?', answer }]);

expect(result?.mainEntity[0].acceptedAnswer.text).toBe(answer);
});
});
42 changes: 42 additions & 0 deletions docs/constants/structured-data.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,48 @@ export function buildBreadcrumbListSchema(items: BreadcrumbItem[]) {
};
}

type TechArticleInput = {
title: string;
description?: string;
modificationDate?: string;
url: string;
};

export function buildTechArticleSchema({
title,
description,
modificationDate,
url,
}: TechArticleInput) {
return {
'@context': 'https://schema.org',
'@type': 'TechArticle',
headline: title,
...(description ? { description } : {}),
...(modificationDate ? { dateModified: modificationDate } : {}),
publisher: websiteSchema.publisher,
url,
};
}

type FAQItem = { question: string; answer: string };

export function buildFAQPageSchema(items: FAQItem[]) {
if (items.length === 0) {
return null;
}

return {
'@context': 'https://schema.org',
'@type': 'FAQPage',
mainEntity: items.map(item => ({
'@type': 'Question',
name: item.question,
acceptedAnswer: { '@type': 'Answer', text: item.answer },
})),
};
}

export const websiteSchema = {
'@context': 'https://schema.org',
'@type': 'WebSite',
Expand Down
5 changes: 5 additions & 0 deletions docs/pages/bare/overview.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import { Rocket02Icon } from '@expo/styleguide-icons/outline/Rocket02Icon';

import { BoxLink } from '~/ui/components/BoxLink';
import { Collapsible } from '~/ui/components/Collapsible';
import { FAQ } from '~/ui/components/FAQ';
import { CODE } from '~/ui/components/Text';

If you have a React Native app that doesn't use any Expo tools, you might be wondering what Expo can provide for you, why you might want to use Expo tools and services, and how to get started.
Expand Down Expand Up @@ -144,6 +145,8 @@ The following helps with your project's long term maintainability, native code m

## Common questions

<FAQ>

<Collapsible summary="How long will it take to adopt Expo in my existing React Native project?">

Adopting Expo doesn't have to be done in one step. You can start with the _quick wins_ and then move on to more complex parts. You can also pick and choose which features you want to adopt based on what is most helpful for your project.
Expand Down Expand Up @@ -207,3 +210,5 @@ Yes, you can install and use third-party libraries that require native projects
You can continue using any navigation library in your project. However, we recommend using Expo Router for all the benefits [described here](/router/introduction).

</Collapsible>

</FAQ>
7 changes: 6 additions & 1 deletion docs/pages/bare/using-expo-cli.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import { BookOpen02Icon } from '@expo/styleguide-icons/outline/BookOpen02Icon';

import { BoxLink } from '~/ui/components/BoxLink';
import { Collapsible } from '~/ui/components/Collapsible';
import { FAQ } from '~/ui/components/FAQ';
import { Terminal } from '~/ui/components/Snippet';
import { DEMI } from '~/ui/components/Text';

Expand All @@ -32,7 +33,7 @@ Expo CLI commands provide several benefits over the similar commands in `@react-

- Instant access to Hermes debugger with <kbd>j</kbd> keystroke.
- The debugger ships with [React Native DevTools](/debugging/tools/#debugging-with-react-native-devtools).
- [Continuous Native Generation (CNG)](/workflow/continuous-native-generation/) support with [`expo prebuild`](/workflow/prebuild/) for upgrades, white-labeling, easy third-party package setup, and better maintainability of the codebase (by reducing the surface area).
- [Continuous Native Generation (CNG)](/workflow/continuous-native-generation/) support with [`expo prebuild`](/more/glossary-of-terms/#prebuild) for upgrades, white-labeling, easy third-party package setup, and better maintainability of the codebase (by reducing the surface area).
- Support for file-based routing with [`expo-router`](/router/introduction/).
- [Async bundling](/router/web/async-routes) in development.
- Built-in [environment variable support](/guides/environment-variables) and **.env** file integration.
Expand Down Expand Up @@ -71,6 +72,8 @@ When building your project, you can choose a device or simulator by using the `-

## Common questions

<FAQ>

<Collapsible summary="Can I use Expo CLI without installing the Expo Modules API?">

Expo Modules API is also installed when you install the `expo` package with `npx install-expo-modules`. If you want to try out Expo CLI for now without installing Expo Modules API, install the `expo` package with `npm install` and then configure the **react-native.config.js** to exclude the package from autolinking:
Expand Down Expand Up @@ -99,6 +102,8 @@ Yes! Refer to the [Customized Prebuild Example repository](https://github.com/by

</Collapsible>

</FAQ>

## Next steps

Now, with the `expo` package installed and configured in your project, you can start using all features from Expo CLI and SDK. Here are some recommended next steps to dive deep:
Expand Down
2 changes: 1 addition & 1 deletion docs/pages/build-reference/caching.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -256,7 +256,7 @@ To disable using our CocoaPods cache server for your builds set the `EAS_BUILD_D
}
```

It is typical to not have your project **Podfile.lock** committed to source control when using [prebuild](/workflow/prebuild) to generate your **ios** directory [remotely at build time](/build-reference/ios-builds).
It is typical to not have your project **Podfile.lock** committed to source control when using [prebuild](/more/glossary-of-terms/#prebuild) to generate your **ios** directory [remotely at build time](/build-reference/ios-builds).
It can be useful to cache your **Podfile.lock** to have deterministic builds, but the tradeoff in this case is that, because you don't use the lockfile during local development, your ability to determine when a change is needed and to update specific dependencies is limited.
If you cache this file, you may occasionally end up with build errors that require clearing the cache.
To cache **Podfile.lock**, add **./ios/Podfile.lock** to the `cache.paths` list in your build profile in **eas.json**.
Expand Down
2 changes: 1 addition & 1 deletion docs/pages/build-reference/easignore.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ Copy the content of the **.gitignore** file into the **.easignore** file. Then,
/coverage
```

If your project does not contain **android** and **ios** directories, [EAS Build will run Prebuild](/workflow/prebuild/#usage-with-eas-build) to generate these native directories before compilation.
If your project does not contain **android** and **ios** directories, [EAS Build will run Prebuild](/workflow/continuous-native-generation/#usage-with-eas-build) to generate these native directories before compilation.

</Step>

Expand Down
2 changes: 1 addition & 1 deletion docs/pages/build-reference/ios-capabilities.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -114,7 +114,7 @@ There are two ways to manually enable Apple capabilities, both systems will requ

### Xcode

> Preferred method for projects that do **not** use [Expo Prebuild](/workflow/prebuild) to continuously generate the native **android** and **ios** directories.
> Preferred method for projects that do **not** use [Expo Prebuild](/more/glossary-of-terms/#prebuild) to continuously generate the native **android** and **ios** directories.

1. Open the **ios** directory in Xcode with `xed ios`. If you don't have an **ios** directory, run `npx expo prebuild -p ios` to generate one.
2. Then follow the steps mentioned in [Add a capability][apple-enable-capability].
Expand Down
2 changes: 1 addition & 1 deletion docs/pages/build-reference/local-builds.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@ Some of the options available for cloud builds are not available locally. Limita

## App compilation for development and production builds locally

To compile your app locally for development with Expo CLI, use `npx expo run:android` or `npx expo run:ios` commands instead. If you use [Continuous Native Generation](/workflow/continuous-native-generation), you can also run [prebuild](/workflow/prebuild) to generate your **android** and **ios** directories and then proceed to open the projects in the respective IDEs and build them like any native project. For more details, see:
To compile your app locally for development with Expo CLI, use `npx expo run:android` or `npx expo run:ios` commands instead. If you use [Continuous Native Generation](/workflow/continuous-native-generation), you can also run [prebuild](/more/glossary-of-terms/#prebuild) to generate your **android** and **ios** directories and then proceed to open the projects in the respective IDEs and build them like any native project. For more details, see:

<BoxLink
title="Local app development"
Expand Down
5 changes: 5 additions & 0 deletions docs/pages/build/introduction.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import { Cube01Icon } from '@expo/styleguide-icons/outline/Cube01Icon';
import { BoxLink } from '~/ui/components/BoxLink';
import { Collapsible } from '~/ui/components/Collapsible';
import { NoIcon, YesIcon } from '~/ui/components/DocIcons';
import { FAQ } from '~/ui/components/FAQ';
import { Terminal } from '~/ui/components/Snippet';

**EAS Build** is a hosted Expo Application Services (EAS) service that builds app binaries (also called [standalone apps](/more/glossary-of-terms/#standalone-app)) for your Expo and React Native projects.
Expand Down Expand Up @@ -52,6 +53,8 @@ This command sends your project to EAS Build and produces installable binaries f

## Frequently asked questions

<FAQ>

<Collapsible summary="How do I share builds with my team before submitting to app stores?">

Use [internal distribution](/build/internal-distribution/) to share builds with a URL. Set `"distribution": "internal"` in your [build profile](/build/eas-json/#build-profiles) in **eas.json** to generate installable Android Package (APK) files for Android or [ad hoc builds](/build/internal-distribution/) for iOS.
Expand Down Expand Up @@ -111,6 +114,8 @@ Android builds run on Linux runners hosted in Google Cloud Platform, and iOS bui

</Collapsible>

</FAQ>

## Get started

<BoxLink
Expand Down
2 changes: 1 addition & 1 deletion docs/pages/core-concepts.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ The `expo` npm package enables a suite of incredible features for React Native a
<BoxLink
title="Prebuild"
description="Separate React from Native to develop from any computer, upgrade easily, white label apps, and maintain larger projects."
href="/workflow/prebuild/"
href="/workflow/continuous-native-generation/"
Icon={BookOpen02Icon}
/>
<BoxLink
Expand Down
2 changes: 1 addition & 1 deletion docs/pages/deploy/build-project.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -129,7 +129,7 @@ Learn more about common patterns with the [workflows examples guide](/eas/workfl

To create a release (also known as production) build locally, see the following React Native guides for more information on the necessary steps for Android and iOS.

These guides assume your project has **android** and/or **ios** directories containing the respective native projects. If you use [Continuous Native Generation](/workflow/continuous-native-generation) then you will need to run [prebuild](/workflow/prebuild) to generate the directories before following the guides.
These guides assume your project has **android** and/or **ios** directories containing the respective native projects. If you use [Continuous Native Generation](/workflow/continuous-native-generation) then you will need to run [prebuild](/more/glossary-of-terms/#prebuild) to generate the directories before following the guides.

> **Note**: Following the guide below, in step four, when you build the release **.aab** for Android, run `./gradlew app:bundleRelease` from the **android** directory instead of `npx react-native build-android --mode=release`.

Expand Down
2 changes: 1 addition & 1 deletion docs/pages/develop/tools.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ The following is a list of common commands that you will use with Expo CLI while
| Command | Description |
| ------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `npx expo start` | Starts the development server (whether you are using a development build or Expo Go). |
| `npx expo prebuild` | Generates native Android and iOS directories using [Prebuild](/workflow/prebuild/). |
| `npx expo prebuild` | Generates native Android and iOS directories using [Prebuild](/workflow/continuous-native-generation/). |
| `npx expo run:android` | Compiles native Android app locally. |
| `npx expo run:ios` | Compiles native iOS app locally. |
| `npx expo install package-name` | Used to install a new library or validate and update specific libraries in your project by adding `--fix` option to this command. |
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -135,7 +135,7 @@ To test your new splash screen, build your app for [internal distribution](/tuto

<Collapsible summary="Not using prebuild?">

If your app does not use [Expo Prebuild](/workflow/prebuild) (formerly the _managed workflow_) to generate the native **android** and **ios** directories, then changes in the app config will have no effect. For more information, see [how you can customize the configuration manually](https://github.com/expo/expo/tree/main/packages/expo-splash-screen#-installation-in-bare-react-native-projects).
If your app does not use [Expo Prebuild](/more/glossary-of-terms/#prebuild) (formerly the _managed workflow_) to generate the native **android** and **ios** directories, then changes in the app config will have no effect. For more information, see [how you can customize the configuration manually](https://github.com/expo/expo/tree/main/packages/expo-splash-screen#-installation-in-bare-react-native-projects).

</Collapsible>

Expand Down
5 changes: 5 additions & 0 deletions docs/pages/eas-update/codepush.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ description: A guide to help migrate from CodePush to EAS Update.
---

import { Collapsible } from '~/ui/components/Collapsible';
import { FAQ } from '~/ui/components/FAQ';
import { Terminal } from '~/ui/components/Snippet';
import { Step } from '~/ui/components/Step';

Expand Down Expand Up @@ -69,6 +70,8 @@ After successfully submitting your app, users will be able to download and use t

## Common questions

<FAQ>

<Collapsible summary="How do I release mandatory/critical updates with EAS Update?">

CodePush CLI has a `--mandatory` flag that allows you to release mandatory updates. You can build this functionality with EAS Update but there is no specific flag for it.
Expand Down Expand Up @@ -130,6 +133,8 @@ Yes, EAS Update supports end-to-end code signing. It is available for EAS Produc

</Collapsible>

</FAQ>

## Conceptual differences between CodePush and EAS Update

CodePush and EAS Update are both services that allow you to send hotfixes to the JavaScript code of your app, but they take slightly different approaches, and so you may need to adapt your release process when moving to EAS Update.
Expand Down
Loading
Loading