diff --git a/docs/docs/adding-search-with-algolia.md b/docs/docs/adding-search-with-algolia.md index 9d7fefde6cf39..de148be4fb2e5 100644 --- a/docs/docs/adding-search-with-algolia.md +++ b/docs/docs/adding-search-with-algolia.md @@ -313,7 +313,7 @@ You now need to hook up the two components to each other and perform the actual ```jsx:title=src/components/search/index.js import algoliasearch from "algoliasearch/lite" -import { createRef, default as React, useState } from "react" +import { createRef, default as React, useState, useMemo } from "react" import { InstantSearch } from "react-instantsearch-dom" import { ThemeProvider } from "styled-components" import StyledSearchBox from "./styled-search-box" @@ -331,9 +331,13 @@ export default function Search({ indices }) { const rootRef = createRef() const [query, setQuery] = useState() const [hasFocus, setFocus] = useState(false) - const searchClient = algoliasearch( - process.env.GATSBY_ALGOLIA_APP_ID, - process.env.GATSBY_ALGOLIA_SEARCH_KEY + const searchClient = useMemo( + () => + algoliasearch( + process.env.GATSBY_ALGOLIA_APP_ID, + process.env.GATSBY_ALGOLIA_SEARCH_KEY + ), + [] ) useClickOutside(rootRef, () => setFocus(false)) @@ -362,6 +366,8 @@ The `ThemeProvider` exports variables for the CSS to use (this is the [theming]( The `hasFocus` variable tracks whether the search box is currently in focus. When it is, it should display the input field (if not, only the search icon button is visible). +The `searchClient` variable is [memoized](https://reactjs.org/docs/hooks-reference.html#usememo) to avoid re-creating the Algolia search client when the `Search` component is re-rendered. This is important for performance, as the client caches searches to minimise the number of requests made to Algolia. + `StyledSearchRoot` is the root of the whole component. The React hook `useClickOutside` provides a callback if the user clicks anywhere else on the page, in which case it should close. `InstantSearch` from [`react-instantsearch-dom`](https://community.algolia.com/react-instantsearch) wraps the search box and search results to orchestrate the search. diff --git a/docs/docs/conceptual/image-plugin-architecture.md b/docs/docs/conceptual/image-plugin-architecture.md new file mode 100644 index 0000000000000..edf6ade97fb57 --- /dev/null +++ b/docs/docs/conceptual/image-plugin-architecture.md @@ -0,0 +1,113 @@ +# Architecture of Gatsby's image plugins + +This is a technical document for anyone who is interested in how images are implemented in Gatsby. This is not for learning [how to use images in Gatsby](/docs/how-to/images-and-media/using-gatsby-plugin-image/), or even [how to add image support to a plugin](/docs/how-to/plugins-and-themes/adding-gatsby-image-support/). This is for if you are working on Gatsby, or if you're curious about how it's implemented. + +## The image plugins + +Image processing in Gatsby has a large number of parts, with `gatsby-plugin-image` serving as a core, but using several other plugins in order to perform its job. We have tried to simplify this for end-users by documenting most of the features as if they were all part of `gatsby-plugin-image`, rather than trying to document the borders of responsibility between `gatsby-plugin-image`, `gatsby-plugin-sharp`, `gatsby-transformer-sharp` and `gatsby-source-filesystem`. Similarly, `StaticImage` is presented to the user as a React component, and they only need to interact with it as one. However it is in fact the front end to an image processing pipeline that includes three Gatsby plugins, two Babel plugins and several hooks into the Gatsby build process. This document will show exactly what each plugin does, and describe how the main parts are implemented. These are the plugins used for image processing and display. + +### `gatsby-plugin-image` + +This is the main plugin that users interact with. It includes two components for displaying images, as well as helper functions and a toolkit for plugin developers. + +#### The `GatsbyImage` component + +This is a React component, and is the actual component used to display all images. If somebody uses `StaticImage`, or an image component from a CMS provider, they all use `GatsbyImage` under the hood for the actual image display. + +#### The `StaticImage` component + +A a lightweight wrapper around `GatsbyImage`, this component is detected during the build process and the props are extracted to enable images to be downloaded, and processed by `gatsby-plugin-sharp` without needing GraphQL. + +#### Plugin toolkit + +`gatsby-plugin-image` includes several functions that are used by third-party source plugins to enable them to generate image data objects. This includes helpers in `gatsby-plugin-image/graphql-utils`, which help plugin authors create `gatsbyImageData` resolvers, as well as the `getImageData` function used to generate image data at runtime by plugins with URL-based image resizing APIs. It does not perform any actual image processing (and doesn't require sharp), but does ensure that the correct object is generated with all of the correct image sizes. It includes the `getLowResolutionImageURL` function which helps when generating blurred placeholders. + +#### Runtime helpers + +The plugin exports a number of other helper functions designed to help end-users work with image data objects at runtime. These include the `getImage`, `getSrc` and `getSrcSet` utilities, as well as the `withArtDirection` helper. + +### `gatsby-plugin-sharp` + +This includes the actual image processing functions from both sharp but also imagemin and potrace. It includes the functions that generate the image data object, including calculating which srcset sizes to generate. It exports `generateImageData`, which is used by `gatsby-transformer-sharp` and `gatsby-plugin-image`. It takes a `File` node and the image processing arguments, calculates which images to generate, processes these images and returns an image data object suitable for passing to `GatsbyImage`. It also exports helper functions for third party plugins to use, such as `traceSVG`. + +### `gatsby-transformer-sharp` + +This plugin attaches a `childImageSharp` node to all image `File` nodes. This includes the `gatsbyImageData` resolver, which uses `generateImageData` from `gatsby-plugin-sharp` to process images and return an image data object suitable for passing to `GatsbyImage`. The node also includes legacy `fixed` and `fluid` resolvers for the old `gatsby-image` component. + +### `gatsby-core-utils` + +Third party plugin authors can use the `fetchRemoteFile` function to download files and make use of Gatsby's caching without needing to create a file node. This is used for low-resolution placeholder images. + +### `gatsby-source-filesystem` + +The image plugin uses `createRemoteFileNode` when a `StaticImage` component has a remote URL as `src`. It does not currently use `fetchRemoteFile`, because `generateImageData` requires a `File` object. + +### Third-party plugins + +Many source plugins now support `GatsbyImage`. They do this by generating image data objects that can be passed to the `GatsbyImage` component, or by providing their own components that wrap it. This data is either returned from a custom `gatsbyImageData` resolver on the plugin's nodes, or via a runtime helper that accepts data from elsewhere. An example of the latter would be the new Shopify plugin, which includes a `getShopifyImage` function that can be used to generate images from the Shopify search or cart APIs. These plugins all use the plugin toolkit from `gatsby-plugin-image` to ensure that the object they create are compatible. These do not require sharp, as the CMS or CDN provider handles the resizing. The API for each plugin's `gatsbyImageData` resolver will depend on the individual plugin, but authors are encouraged to provide an API similar to the one from `gatsby-transformer-sharp`. + +## How `GatsbyImage` works + +`GatsbyImage` is a React component used to display performant, responsive images in Gatsby. It is used under the hood by all other compatible components such as `StaticImage` and components from CMS source plugins. It uses several performance tricks, and deserves most of the credit for improved performance scores when switching to the image plugin. + +### Anatomy of the component + +The `GatsbyImage` component wraps several other components, which are all exported by the plugin. It was originally designed to allow users to compose their own custom image components, but we have not documented this, so it should currently be considered unsupported. It is something that could be looked-at in future, but until that point `GatsbyImage` and `StaticImage` should be considered the only public components. + +#### Lazy hydration + +Throughout the component there are different versions delivered for browser and server. The reason for this is that the component performs lazy hydration: the image is loaded as soon as the SSR HTML is loaded in the browser, including blur-up and lazy-loaded images. Hydration is usually the slowest part of a React app's page load. `GatsbyImage` avoids this by skipping React for all initial image loads, leading to faster LCP. The plugin uses `onRenderBody` in `gatsby-ssr` to inject inline script and CSS tags to enable this. The JS attaches a `load` event listener to the body, which hides the placeholder and shows the main image when any `GatsbyImage` has loaded. It uses `