diff --git a/docs/common/error-utilities.ts b/docs/common/error-utilities.ts index 8f822a8b46a59..4e92956f36e38 100644 --- a/docs/common/error-utilities.ts +++ b/docs/common/error-utilities.ts @@ -356,6 +356,7 @@ const RENAMED_PAGES: Record = { '/versions/latest/sdk/in-app-purchases/': '/guides/in-app-purchases/', '/versions/v50.0.0/sdk/in-app-purchases/': '/guides/in-app-purchases/', '/guides/web-performance/': '/guides/analyzing-bundles/', + '/guides/assets/': '/develop/user-interface/assets/', // Classic updates moved to archive '/guides/configuring-ota-updates/': '/archive/classic-updates/getting-started/', diff --git a/docs/constants/navigation.js b/docs/constants/navigation.js index 07cc7cf41c7ac..e25b5f09d7bc4 100644 --- a/docs/constants/navigation.js +++ b/docs/constants/navigation.js @@ -65,6 +65,7 @@ const home = [ makePage('develop/user-interface/app-icons.mdx'), makePage('develop/user-interface/safe-areas.mdx'), makePage('develop/user-interface/fonts.mdx'), + makePage('develop/user-interface/assets.mdx'), makePage('develop/user-interface/color-themes.mdx'), makePage('develop/user-interface/animation.mdx'), makePage('develop/user-interface/store-data.mdx'), diff --git a/docs/deploy.sh b/docs/deploy.sh index 5592825c3cfa5..194c155c488d5 100755 --- a/docs/deploy.sh +++ b/docs/deploy.sh @@ -170,6 +170,7 @@ redirects[get-started/errors]=debugging/errors-and-warnings redirects[develop/development-builds/parallel-installation]=build-reference/variants redirects[home/develop/user-interface/safe-areas]=develop/user-interface/safe-areas redirects[home/develop/development-builds/introduction]=develop/development-builds/introduction +redirects[guides/assets]=develop/user-interface/assets # Redirects after Guides organization redirects[guides]=guides/overview diff --git a/docs/pages/develop/user-interface/assets.mdx b/docs/pages/develop/user-interface/assets.mdx new file mode 100644 index 0000000000000..f2e51928462f5 --- /dev/null +++ b/docs/pages/develop/user-interface/assets.mdx @@ -0,0 +1,123 @@ +--- +title: Assets +description: Learn about using static assets in your project, including images, videos, sounds, database files, and fonts. +--- + +Files such as images, videos, sounds, database files for SQLite, or fonts are considered **static assets**. They are not part of your project's JavaScript but are part of your app. These assets can be served locally from your project or remotely over the network. + +## Serve an asset locally + +When an asset is stored in your project's file system, it can be embedded in your app binary at build time or loaded at runtime. You can import them like JavaScript modules using `require` or `import` statements. + +For example, to render an image called **example.png** in **App.js**, you can directly use `require` to import the image from the project's **assets/images** directory and pass it to the `` component: + +```jsx App.js + +``` + +The bundler automatically provides a width and height for the images imported, as shown in the above example. For more information, see [Static Image Resources](https://reactnative.dev/docs/images#static-image-resources). + +Libraries such as `expo-image` and `expo-file-system` work similarly to the `` component with local assets. + +### How are assets served locally + +Locally stored assets are served over HTTP in development. They are automatically bundled into your app binary at the build time for production apps and served from disk on a device. + +### Load an asset at build time + +> **Note:** The `expo-asset` config plugin is only available for SDK 51 and above. If you are using an older SDK, you can load a [using the `useAssets` hook](/versions/v51.0.0/sdk/asset/#useassetsmoduleids). + +Install the `expo-asset` library and add the config plugin to the app config file. This plugin will embed the asset file in your native project. + +```json app.json +{ + "plugins": [ + [ + "expo-asset", + + { + "assets": ["./assets/images/example.png"] + } + ] + ] +} +``` + +The `assets` option takes an array of one or more asset files or directory names to link the file to the native project. The path to each file is relative to the project's root. + +When you [create a new native build](/develop/development-builds/create-a-build/), you can import and use the asset in your project without using a `require` or an `import` statement. + +For example, the **example.png** is linked by the above config plugin. You can directly import it into your component and use its resource name. + +```jsx App.js +import { Image } from 'expo-image'; +/* @hide ... */ /* @end */ + +function App() { + return ; +} +``` + +The above example is quite concise. However, when a native API expects a specific file and its resource name, this method makes it convenient to integrate with that API and use the resource name directly. + +> **info** For more information on supported file formats used with the config plugin, see [Assets API reference](/versions/v51.0.0/sdk/asset/#configurable-properties). + +### Load an asset at runtime + +Install the `expo-asset` library in your project. Once the installation step is complete, import the `useAssets` hook from the `expo-asset` library. The hook downloads and stores an asset locally, and after the asset is loaded, it returns a list of that asset's instances. + +```jsx App.js +import { useAssets } from 'expo-asset'; + +export default function App() { + const [assets, error] = useAssets([ + require('path/to/example-1.jpg'), + require('path/to/example-2.png'), + ]); + + return assets ? : null; +} +``` + +## Serve an asset remotely + +When an asset is served remotely, it is not bundled into the app binary at build time. You can use the URL of the asset resource in your project if it is hosted remotely. For example, pass the URL to the `` component to render a remote image. + +```jsx App.js +import { Image } from 'expo-image'; +/* @hide ... */ /* @end */ + +function App() { + return ( + + ); +} +``` + +There is no guarantee about the availability of images served remotely using a web URL because an internet connection may not be available, or the asset might be removed. + +Additionally, loading assets remotely also requires you to provide metadata about the asset. In this example, the bundler cannot retrieve the image's width and height, so you have to pass that explicitly to the `` component. If you don't, the image will default to 0px by 0px. + +## Manual optimization methods + +### Images + +You can compress images using the following: + +- [`guetzli`](https://github.com/google/guetzli) +- [`pngcrush`](https://pmt.sourceforge.io/pngcrush/) +- [`optipng`](http://optipng.sourceforge.net/) + +Some image optimizers are lossless. They re-encode your image to be smaller without any change or loss in the pixels displayed. When you need each pixel to be untouched from the original image, a lossless optimizer and a lossless image format like PNG are a good choice. + +Other image optimizers are lossy. The optimized image differs from the original image. Often, lossy optimizers are more efficient because they discard visual information that reduces file size while making the image look nearly identical to humans. Tools like `imagemagick` can use comparison algorithms like [SSIM](https://en.wikipedia.org/wiki/Structural_similarity) to show how similar two images look. It's quite common for an optimized image that is over 95% similar to the original image to be far less than 95% of the original file size. + +### Fonts + +See [Wait for fonts to load in the Fonts guide](/develop/user-interface/fonts/#wait-for-fonts-to-load) for more information. + +### Other assets + +For assets like GIFs or movies, or non-code and non-image assets, it's up to you to optimize and minify those assets. + +> **Note**: GIFs are a very inefficient format. Modern video codecs can produce significantly smaller file sizes with better quality. diff --git a/docs/pages/develop/user-interface/fonts.mdx b/docs/pages/develop/user-interface/fonts.mdx index 82f222679a084..4f56370fa60f5 100644 --- a/docs/pages/develop/user-interface/fonts.mdx +++ b/docs/pages/develop/user-interface/fonts.mdx @@ -18,7 +18,7 @@ The first thing you need is a font file. For a working example, we are going to ### Supported font formats -The two officially supported font formats that work consistently in the Expo SDK across Android, iOS and the web, are OTF and TTF. If your font is in another format, you will require to set up an [advanced configuration](#beyond-otf-and-ttf) for your project. +The two officially supported font formats that work consistently in the Expo SDK across Android, iOS and the web, are OTF and TTF. If your font is in another format, you will be required to set up an [advanced configuration](#beyond-otf-and-ttf) for your project. If you have both OTF and TTF versions of a font, prefer OTF. OTF is a newer format and **.otf** files are often smaller than **.ttf** files. Sometimes OTF files render slightly better in certain contexts. In general, both formats are very similar and perfectly acceptable. @@ -258,9 +258,7 @@ export default function App() { return ( - - Inter Black - + Inter Black ); } @@ -278,21 +276,23 @@ const styles = StyleSheet.create({ ## Wait for fonts to load -If you [embed your fonts in your native project](#embed-the-font-in-your-native-project), they will be available immediately without any additional code outside the config plugin. However, if you load your fonts at runtime, they will not be available immediately. So, it is generally a good practice not to render anything until the font is ready. Instead, you can continue to display the Splash Screen of your app until all fonts have loaded (or an error has been returned). It is done using [`expo-splash-screen`](/versions/latest/sdk/splash-screen/) package. See the [minimal example](#minimal-example) section on how to use it. +If you [embed your fonts in your native project](#embed-font-in-a-native-project), they will be available immediately without any additional code outside the config plugin. However, if you load your fonts at runtime, they will not be available immediately. -### Load fonts on the web +It's common to prevent rendering an app until the font is ready. You can accomplish this by displaying the Splash Screen of your app until all fonts have loaded (or an error has been returned). See the [`expo-splash-screen`](/versions/latest/sdk/splash-screen/) package for more. Additionally, see the [minimal example](#minimal-example) section on how to use it. -Sometimes, particularly on the web people choose to render their content in a platform default font while their custom font is loading. Alternatively, to render the rest of their content, that doesn't depend on the custom font while the font is loading. These approaches are called FOUT and FOIT and you can read a lot more about them on the web. +### Handle `@expo/vector-icons` initial load -In general, these strategies are not recommended for native apps. If you include your fonts in your project, the fonts will always be delivered to the user by the time your code is running. The one exception to this is that you may prefer to do this on the web. +Before `@expo/vector-icons` icons load for the first time, they'll appear as invisible icons in your app. Once they load, they'll be cached for all subsequent uses of the app. To avoid showing invisible icons on your app's first load, you can preload fonts during the initial loading screen with [`useFonts`](/versions/latest/sdk/font/#usefontsmap). For example: -## Additional information +```jsx App.js +useFonts([require('./assets/fonts/ComicSans.ttf', FontAwesome.font)]); +``` -You probably don't need to know anything beyond this point to use custom fonts effectively in your app. If you are curious or your use case has not been addressed by the above information, please continue reading. +## Additional information ### Loading a remote font directly from the web -In general, it's best and safest to load fonts from your local assets. If you submit to app stores, they will be bundled with the download and available immediately. You don't have to worry about CORS or other potential issues. +In general, it's best and safest to load fonts from your local assets. If you submit your app to app stores, the fonts will be bundled with the download and available immediately. You don't have to worry about CORS or other potential issues. However, if you to load a remote font file directly from the web rather than from your project's assets, you can do it by replacing the `require('./assets/fonts/MyFont.otf')` with the URL of your font. See the below example: @@ -319,7 +319,7 @@ export default function App() { ); } -/* @hide const styles = StyleSheet.create({ ... }); */ + const styles = StyleSheet.create({ container: { flex: 1, @@ -327,7 +327,6 @@ const styles = StyleSheet.create({ alignItems: 'center', }, }); -/* @end */ ``` @@ -383,7 +382,6 @@ export default class App extends React.Component { } } -/* @hide const styles = StyleSheet.create({ ... }); */ const styles = StyleSheet.create({ container: { flex: 1, @@ -391,7 +389,6 @@ const styles = StyleSheet.create({ alignItems: 'center', }, }); -/* @end */ ``` diff --git a/docs/pages/eas-update/optimize-assets.mdx b/docs/pages/eas-update/optimize-assets.mdx index e2778449e5594..eab9d24113ded 100644 --- a/docs/pages/eas-update/optimize-assets.mdx +++ b/docs/pages/eas-update/optimize-assets.mdx @@ -39,21 +39,7 @@ After running the command, all image assets are compressed except the ones that ### Other manual optimization methods -You can also compress images using any other online service or a tool such as: - -- [`guetzli`](https://github.com/google/guetzli) -- [`pngcrush`](https://pmt.sourceforge.io/pngcrush/) -- [`optipng`](http://optipng.sourceforge.net/) - -Some image optimizers are lossless. They re-encode your image to be smaller without any change or loss in the pixels displayed. When you need each pixel to be untouched from the original image, a lossless optimizer and a lossless image format like PNG are a good choice. - -Other image optimizers are lossy. The optimized image differs from the original image. Often, lossy optimizers are more efficient because they discard visual information that reduces file size while making the image look nearly identical to humans. Tools like `imagemagick` can use comparison algorithms like [SSIM](https://en.wikipedia.org/wiki/Structural_similarity) to show how similar two images look. It's quite common for an optimized image that is over 95% similar to the original image to be far less than 95% of the original file size. - -## Other assets - -For assets like GIFs or movies, or non-code and non-image assets, it's up to the developer to optimize and minify those assets. - -> **Note**: GIFs are a very inefficient format. Modern video codecs can produce smaller file sizes by over an order of magnitude. +To optimize images and videos manually, see [Assets](/develop/user-interface/assets/#manual-optimization-methods) for more information. ## Ensuring assets are included in updates diff --git a/docs/pages/guides/assets.mdx b/docs/pages/guides/assets.mdx deleted file mode 100644 index 0dfc15857312e..0000000000000 --- a/docs/pages/guides/assets.mdx +++ /dev/null @@ -1,60 +0,0 @@ ---- -title: Assets -description: Learn about using local and remote assets. ---- - -Images, fonts, videos, sounds, and any other file that your app depends on that is not JavaScript are considered to be an **asset**. - -## Local assets - -Assets stored on your file system can be imported like JavaScript modules, using `require` or `import`. For example, from **App.js**, to render an image called **example.png** that is stored within the project's **assets/images** directory you can use `require` to import the image and then pass it to the `Image` component: - -```jsx App.js - -``` - -The bundler will automatically provide a width and height for images imported this way. For more information, see [Static Image Resources](https://reactnative.dev/docs/images#static-image-resources). - -Libraries like [`expo-av`](/versions/latest/sdk/video/) and [`expo-file-system`](/versions/latest/sdk/filesystem/) work similar to `Image` with local assets. - -Local assets this way will be served over HTTP in development, and they will be automatically bundled into your app binary at build time. - -When you publish an update to your app with [EAS Update](/eas-update/introduction/), your assets will be uploaded and served from a CDN. Any asset that is not already included in the build that receives the update will be downloaded before launching the update. When testing an app in Expo Go, assets will be downloaded as the project is running. You can load these before your app starts with the [`useAssets`](/versions/latest/sdk/asset/) hook. - -## Remote assets - -To render a remote image, pass the URL to the image to the `Image` component: - -```jsx App.js - -``` - -No guarantee can be made about the availability of the images that you refer to with a web URL because an internet connection may not be available or the asset may have been removed. Additionally, we don't have the same amount of information about arbitrary web URLs: when your assets are available on the local filesystem, the bundler can read some metadata (for example, width and height) and pass that through to your app, so you don't need to specify a width and height. When specifying a remote web URL, you will need to explicitly specify a width and height, or it will default to 0x0. Lastly, as you will see later, caching behavior is different in both cases. - -Libraries like [`expo-av`](/versions/latest/sdk/video/) and [`expo-file-system`](/versions/latest/sdk/filesystem/) work similar to `Image` with local assets. - -Remote assets are not bundled into binaries at build time. They are also not considered part of an [EAS Update](/eas-update/introduction/) bundle. - -## Customizing supported asset extensions - -If you need to import a file type that is not recognized by Metro by default, you can modify the Metro configuration. For example, you may want to use the `.db` to import a database into your app. Read more about how to customize the asset extensions supported by Metro for Android and iOS in [Customizing Metro](/guides/customizing-metro). - -## Optimization - -### Images - -You can compress images using tools such as [guetzli](https://github.com/google/guetzli), [pngcrush](https://pmt.sourceforge.io/pngcrush/), or [optipng](http://optipng.sourceforge.net/). [imagemin](https://github.com/imagemin/imagemin) is another program and JS library that supports plugins for various optimizers. Many online services can optimize your images for you. - -Some image optimizers are lossless. This means they re-encode your image to be smaller without any change, or loss, in the pixels that are displayed. When you need each pixel to be untouched from the original image, a lossless optimizer and a lossless image format like PNG is a good choices. - -Other image optimizers are lossy, which means the optimized image is different than the original image. Often, lossy optimizers are more efficient because they discard visual information that reduces file size while making the image look nearly the same to humans. Tools like `imagemagick` can use comparison algorithms like [SSIM](https://en.wikipedia.org/wiki/Structural_similarity) to give a sense of how similar two images look. It's quite common for an optimized image that is over 95% similar to the original image to be far less than 95% of the original file size! - -### Fonts - -As explained above, in production builds, your local assets will all be bundled and loaded from disk rather than over the network as expected for native apps, but they must be loaded over the network when previewing in development builds. On the web, the font loading problem is known by: [Flash of Unstyled Text (FOUT), Flash of Invisible Text (FOIT), and Flash of Faux Text (FOFT)](https://css-tricks.com/fout-foit-foft/). - -The default behavior with [`@expo/vector-icons`](icons.mdx#icons) icons is a FOIT on the first load, and on subsequent loads, the font will be automatically cached. You may want to preload fonts during the initial loading screen with [`useFonts`](/versions/latest/sdk/font/#usefontsmap). For example: - -```jsx App.js -useFonts([require('./assets/fonts/ComicSans.ttf', FontAwesome.font)]); -``` diff --git a/docs/pages/guides/customizing-metro.mdx b/docs/pages/guides/customizing-metro.mdx index b787f8029ccc3..8cc8397e9b84c 100644 --- a/docs/pages/guides/customizing-metro.mdx +++ b/docs/pages/guides/customizing-metro.mdx @@ -33,7 +33,7 @@ See [**metro.config.js** documentation](https://facebook.github.io/metro/docs/co ## Assets -Metro resolves files as either source code or assets. Source code is JavaScript, TypeScript, JSON, and other files used by your application. [Assets](/guides/assets) are images, fonts, and other files that should not be transformed by Metro. To accommodate large-scale codebases, Metro requires all extensions for both source code and assets to be explicitly defined before starting the bundler. This is done by adding the `resolver.sourceExts` and `resolver.assetExts` options to the Metro configuration. By default, the following extensions are included: +Metro resolves files as either source code or assets. Source code is JavaScript, TypeScript, JSON, and other files used by your application. [Assets](/develop/user-interface/assets/) are images, fonts, and other files that should not be transformed by Metro. To accommodate large-scale codebases, Metro requires all extensions for both source code and assets to be explicitly defined before starting the bundler. This is done by adding the `resolver.sourceExts` and `resolver.assetExts` options to the Metro configuration. By default, the following extensions are included: - [`resolver.assetExts`](https://github.com/facebook/metro/blob/7028b7f51074f9ceef22258a8643d0f90de2388b/packages/metro-config/src/defaults/defaults.js#L15) - [`resolver.sourceExts`](https://github.com/facebook/metro/blob/7028b7f51074f9ceef22258a8643d0f90de2388b/packages/metro-config/src/defaults/defaults.js#L53)