# Migrate from Resizer v1 to v2

Source: https://dev.arcxp.com/photo-center/image-resizer/migrate-from-resizer-v1-to-v2/

Key changes include moving to a more powerful and reliable option resizing service and the ability for you to add SEO-friendly names for images, which can help your site's SEO performance. Additionally, with the new Resizer, the authentication token is included in the image ANS and is good for all resize variations, so you won't have to worry about storing the secret key.

Purpose 

This guide provides an overview of Resizer v2 features and the necessary actions and configurations to start using Resizer v2 within your Arc XP organization

Audience 

Developers and technically-savvy users who have worked with Resizer v1 and want to migrate to Resizer v2

Arc XP has moved to a newer version of Resizer (v2) as part of the continuous improvement efforts of our global SaaS platform. Key changes include the following:

- **Simplified authentication.** Do not worry about storing secret keys. The authentication token is now included in the `auth` field of the image’s Arc XP Native Specification (ANS) data whenever you upload an image to Photo Center, and is good for all resize variations. For existing images, you can generate the token on-demand by using the [Signing Service API](https://dev.arcxp.com/api/signing-service-api/).
- **Integration of SEO-friendly names.** Photo Center added a new field to image ANS `SEO Filename`, which if populated on import or via API, saves as slugified text and can be prepended to the Arc ID in the construction of Resizer V2 URLs. This improves your site’s SEO performance.
- **New URL format.** Resizer v2 URLs now include the path `/resizer/v2/` as the entryway into Arc XP’s new resizing solution.
- **New query parameters.** Resizer v2 now uses [query parameters](https://dev.arcxp.com/photo-center/image-resizer/resizer-v2-how-to-transform-images/#query-parameters) instead of path parameters. Third-party libraries or packages are no longer necessary to generate Resizer URLs.
- **Improved processing power and reliability** compared to Resizer v1.

## Considerations

Before migrating to Resizer v2, consider that your site’s SEO performance might be affected and that some Resizer v1 URLs could remain on your site after migration. This section provides more details on these considerations.

### SEO Performance Impact

Migrating to Resizer v2 impacts your SEO performance because moving from v1 to v2 requires images to be re-fetched for the first time, transformed at edge, and cached, resulting in unavoidable latency.

#### First Hit

First hit is defined as fetching the pristine image and creating the transformed image (derivative) based on the transformation request.

> **Note:**
> 
> Migrations do not impact your monthly bandwidth or storage billing.

The first hit of Resizer v2 is slower (more latent) than Resizer v1\. However, the pros outweigh this single con that may or may not actually affect your SEO performance. Resizer v2 provides the following benefits:

- More stability than Resizer v1
- Better turnaround time when it comes to reported issues
- More efficient developer workflow for creating image derivatives
- More robust features that can be added in the future

#### Subsequent Hits

After the first hit impact, subsequent and cached hits with Resizer v2 perform on par with Resizer v1 in terms of latency and output performance. We obtained the following results from testing with a beta partner:

|    | TIME          | COUNT | LATENCY (ms) | SIZE (kb) |
| -- | ------------- | ----- | ------------ | --------- |
| v1 | Feb 6 - Feb 7 | 1.31M | 75ms         | 27.3kb    |
| v2 | Oct 4 - Oct 5 | 947k  | 75.9ms       | 27.8kb    |

> **Note:**
> 
> It is important to do this analysis after giving Resizer v2 at least seven days to properly cache and run its course.

### v1 Image URLs

After you migrate to Resizer v2, it is possible that v1 image URLs may still exist and Google bots are still crawling them. Additionally, the migration may accidentally miss v1 image URLs. Resizer v2 is in General Availability on Production. We are adding a redirect so that v1 URLs automatically redirect to v2.

## Requirements

Before taking the migration steps, make sure you meet the following requirements:

- You are able to run PageBuilder locally and make changes to your feature bundle.
- You are familiar with content sources and either have custom content sources or are using Themes-provided content sources. See [PageBuilder Content Source Documentation](https://dev.arcxp.com/pagebuilder-engine/developer-docs/content-source-api/).
- You are familiar with PageBuilder Blocks and are able to update them.
- You are familiar with environment variables and how to add new variables. See [PageBuilder Environment Variables Documentation](https://dev.arcxp.com/pagebuilder-engine/developer-docs/environment-variables/).
- For the following examples, we are going to assume there is no image resizing being used for your code and you are rendering the original images.

## Migration steps

Take the steps in this section to start using Resizer v2.

### 1\. Determine whether you need a phased migration

Arc XP recommends doing a phased migration to Resizer v2 if you are not using out-of-the-box Arc XP Blocks (Themes) or if you are using fully custom blocks. This means updating one or few blocks at a time. Since the Themes 2.0 release, all Arc XP Blocks natively support Resizer v2\. If you remained on the [upgrade path](https://dev.arcxp.com/themes/developer-docs/ejecting-an-arc-block/#deciding-when-to-use-an-out-of-the-box-block-versus-a-custom-block) for your custom blocks, there is no further action needed to migrate to Resizer v2.

A phased migration has the following pros and cons:

**Pros:**

- Allows you to monitor your Resizer v2 implementation at scale for any errors

**Cons:**

- Slower migration timetable
- More overhead work required from development teams

If you do wish to do a phased migration to Resizer v2 on a smaller section of your site, see [Phased migration from Resizer v1 to Resizer v2](https://dev.arcxp.com/photo-center/developer-docs/phased-migration-from-resizer-v1-to-resizer-v2/) for more details.

> **Tip:**
> 
> If you are using an older version of Arc XP Blocks (Themes 1.x), see [Upgrade to v2 Arc XP Blocks](https://dev.arcxp.com/themes/developer-docs/upgrade-to-v2-arc-blocks/) and [image components and blocks deprecations](https://dev.arcxp.com/themes/developer-docs/upgrade-to-v2-arc-blocks/#deprecated-libraries).

### 2\. Add the Resizer v2 environment variables

Resizer v2 requires that you add the following environment variables to your `/environment/*.json` file.

```
{
"RESIZER_TOKEN_VERSION": 1,
"SIGNING_SERVICE_DEFAULT_APP": "resizer",
"RESIZER_URL": "{CONTENT_BASE}/resizer/v2/"
}
```

The `RESIZER_TOKEN_VERSION` value may vary depending on your organization setup. Make a Get request to the following endpoint of the Delivery API:

```
{CONTENT_BASE}/v1/organization/hmac-key/resizer
```

The response includes the field `ssm_version`. You must use this value for `RESIZER_TOKEN_VERSION`.

> **Note:**
>
> If you are using Arc XP Blocks, you need to set additional configurations in the `blocks.json` file. See [Upgrade to v2 Arc XP Blocks](https://dev.arcxp.com/themes/developer-docs/upgrade-to-v2-arc-blocks/#blocksjson)

### 3\. Add the `auth` field to images’ ANS

If you added images to Photo Center before the Resizer v2 or Arc XP Blocks v2 releases, those images need to be _inflated_ to include the `auth` field and the resizer token in their ANS.

Arc XP offers the following methods to inflate ANS objects:

- Make a Get request to the [Signing Service API](https://dev.arcxp.com/api/signing-service-api/) (one request per image, using the image’s encoded URL).
- Activate PageBuilder’s [partial caching](https://dev.arcxp.com/pagebuilder-engine/developer-docs/partial-caching-with-pagebuilder-engine/) (recommended).
- Include the Signing Service API call in content sources. This approach requires you to take the following steps:
1. Update existing content sources to use the `fetch` function (instead of `resolve`).
2. Add the logic to the content source to call the Signing Service API when needed.
The following code block provides an example of an updated content source that uses the updated environment variables:
```js
import axios from "axios";
import {
ARC_ACCESS_TOKEN,
CONTENT_BASE,
SIGNING_SERVICE_DEFAULT_APP,
RESIZER_TOKEN_VERSION,
} from "fusion:environment";
const params = {
id: "text",
service: "text",
serviceVersion: "text",
};
const fetch = ({
id,
service = SIGNING_SERVICE_DEFAULT_APP,
serviceVersion = RESIZER_TOKEN_VERSION,
}) =>
axios({
url: `${CONTENT_BASE}/signing-service/v2/sign/${service}/${serviceVersion}?value=${encodeURIComponent(id)}`,
headers: {
"content-type": "application/json",
Authorization: `Bearer ${ARC_ACCESS_TOKEN}`,
},
method: "GET",
}).then(({ data: content }) => content);
export default {
fetch,
params,
http: false,
// 365 day ttl
ttl: 31536000,
};
```

### 4\. Update custom blocks

This section provides a base example of a block that does not use Resizer v2 and explains the changes that you need to make to the example code in order to use Resizer v2.

#### Base example (without Resizer v2)

The following example uses ANS content to render an image and headline. The image comes from the Promo Items of Composer, and the headline is the Composer headline:

```js
import React from "react";
import PropTypes from "@arc-fusion/prop-types";
import { useContent } from "fusion:content";

const PromoBlock = ({ customFields }) => {
   const content =
      useContent({
         source: customFields?.itemContentConfig?.contentService ?? null,
         query: customFields?.itemContentConfig?.contentConfigValues
            ? {
                  ...customFields.itemContentConfig.contentConfigValues,
              }
            : null,
      }) || null;

   if (!customFields?.itemContentConfig || !content) return null;

   return (
      <article>
         {/*
            The image is using Cloudfront directly - which is not the most
            optimized way to display an image from Photo Center
            Ensure images always go through resizer - see resizer v2
            document for details
         */}
         <img src={content?.promo_items?.basic?.url} alt="" />
         <h2>{content?.headlines?.basic}</h2>
      </article>
   );
};

PromoBlock.propTypes = {
   customFields: PropTypes.shape({
      itemContentConfig: PropTypes.contentConfig("ans-item").tag({
         group: "Configure Content",
         label: "Display Content Info",
      }),
   }),
};

PromoBlock.label = "Promo Block";
PromoBlock.icon = "paragraph-bullets";

export default PromoBlock;
```

#### Example with changes to use Resizer v2

To update the previous example code to use Resizer v2, take the following steps:

1. Import the updated environment variables:

```js
import { RESIZER_URL, RESIZER_TOKEN_VERSION } from "fusion:environment";
```

1. Update the rest of the block to match the following code:
```js
const promoImage = content?.promo_items?.basic;
// Get the Image Auth Token from the `auth` object and use the
// key assigned to auth key 1
const imageAuth = promoImage.auth[RESIZER_TOKEN_VERSION];
// Get the _id of the Image
const imageID = promoImage._id;
let assetId = imageID;
// Append the file extension of the image to assetId if present
if (promoImage.url.split("/").pop().split(".").length > 1) {
  assetId = assetId.concat(".", promoImage.url.split(".").pop());
}
return (
  <article>
    <img src={`${RESIZER_URL}/${assetId}?auth=${imageAuth}&width=300`} alt="" />
    <h2>{content?.headlines?.basic}</h2>
  </article>
);
```

The `img src` is now using Resizer v2 path with its auth token and resizer options instead of the raw image URL.

> **Important:**
>
> You must include logic to handle redirects in `fetch` content sources. Redirects are automatically handled by `resolve` content sources in PageBuilder. For more information, see [Handling redirects in content sources](https://dev.arcxp.com/pagebuilder-engine/developer-docs/handling-redirects-in-content-sources/).

#### Example with content source

This example calls the Content API for a single item and inflates only the promo images image (Featured Media). ANS objects can contain multiple images, and these could also need inflating:

```js
import axios from 'axios';
import {
  CONTENT_BASE,
  ARC_ACCESS_TOKEN,
  RESIZER_TOKEN_VERSION,
} from 'fusion:environment';

// Import the signing service content source
import signingService from './signing-service';

const params = {
  _id: 'text',
};

const fetch = async ({ _id: id, 'arc-site': site }, { cachedCall }) => {
  const contentRequest = await axios({
    url: `${CONTENT_BASE}/content/v4/?_id=${id}${site ? `&website=${site}` : ''}`,
    headers: {
      'content-type': 'application/json',
      Authorization: `Bearer ${ARC_ACCESS_TOKEN}`,
    },
    method: 'GET',
  }).then(({ data: content }) => content);

  // Inflate ANS with token promo items -> basic -> image if there is no auth
  if (contentRequest.promo_items.basic.type === 'image' && !contentRequest.promo_items.basic?.auth?.[RESIZER_TOKEN_VERSION]) {
    contentRequest.promo_items.basic.auth = contentRequest.promo_items.basic.auth || {};
    const signingResponse = await cachedCall(
      'image-token',
      signingService.fetch, // The fetch method imported from the resizer content source
      { query: { id: contentRequest.promo_items.basic._id }, ttl: 31536000, independent: true },
    );
    contentRequest.promo_items.basic.auth[RESIZER_TOKEN_VERSION] = signingResponse.hash;
  }

  return {
    ...contentRequest,
  };
};

export default {
  schemaName: 'ans-item',
  params,
  fetch,
};
```

The previous code does the following:

1. Imports the signing service content source. The code assumes the two content sources are in the same directory within your feature bundle.
2. Imports the `RESIZER_TOKEN_VERSION` environment variable that enables inflating an ANS object.
3. Uses the `fetch` content source pattern with partial caching.
4. Uses `axios` client to make the request to the content API With the response from Content API, the code:
a. Checks if the `promo_items.basic.type` is an image If so, uses the partial caching feature to make a request to the signing service using the signing service content source.
b. Passes the image `_id` to the signing service to get a signed string for the image `_id`.
c. Adds the response of the signing service content source to the content API response.
d. Assigns the signing service response to an `auth` object using the version number as the object key.

With the content source updated to inflate the ANS object with the `auth` data, your blocks do not have to handle getting `auth` tokens. Fetching `auth` tokens on the server provides better performance, but makes use of caching.

## Resizer URL creation

To determine the Resizer URL for your Photo Center images, use these steps:

1. Determine your Resizer URL.
  - If your site has not yet launched, your Resizer URL follows this model `https://{org}-{default-website}-{env}.web.arc-cdn.net/resizer`.
  - If your site has launched, your Resizer URL is `{website name}/resizer`.
2. Add `/v2` to the base Resizer URL: `{website name}/resizer/v2`.
3. Take the image ID from the CloudFront URL of the image and append the ID to the Resizer URL from step 2\. You can find the CloudFront URL either in ANS or the Photo Center UI path for a published image.
  - Example of image ID from CloudFront URL: `SMDVGH3HWRBMRGDZHZFNTCP6EU.jpg`
  - Example of Resizer URL with appended ID: `{website name}/resizer/v2/SMDVGH3HWRBMRGDZHZFNTCP6EU.jpg`
4. Make a `GET` request to the Photo API `photo/api/v2/{photoId}`. Take the Auth token from the image ANS auth field (associated with the image) and paste that as a query parameter at the end of the URL. For example:

```
{website name}/resizer/v2/SMDVGH3HWRBMRGDZHZFNTCP6EU.jpg?auth=db02a059a34fd56e1041d5513ec2745a54239c848a8b
```

> **Tip:**
>
> To see more examples of Resizer URLs and query parameters, see [Transform images with Resizer V2](https://dev.arcxp.com/photo-center/image-resizer/resizer-v2-how-to-transform-images/).

## Non-ANS content

Resizer v2 also supports external images. This section provides an example of a block that has the ability for an editor to specify an external image through a custom field in PageBuilder Editor.

The following block outputs an image and a headline, provided that both are supplied through PageBuilder Editor. The image uses the `imageURL` with no resizing.

```js
import React from "react";
import PropTypes from "@arc-fusion/prop-types";

const ManualPromo = ({ customFields }) => {
  const { imageURL, headline } = customFields;
  if (!imageURL || !headline) return null;

  return (
    <article>
      <img src={imageURL} alt="" />
      <h2>{headline}</h2>
    </article>
  );
};

ManualPromo.propTypes = {
  customFields: PropTypes.shape({
    imageURL: PropTypes.string,
    headline: PropTypes.string,
  }),
};
ManualPromo.label = "Manual Promo Block";
ManualPromo.icon = "paragraph-bullets";

export default ManualPromo;
```

To make the block use Resizer v2, you must update the block to call the signing service content source (see the [Example with content source](#example-with-content-source)). You must also update the image `src` to use the Resizer v2 URL, auth token, and resizer parameters. Take the following steps:

1. Import the `RESIZER_URL` from the environment variables.
2. Import the `useContent` hook from `fusion:content` to enable content source calls within the block. As the signing service is a content source, you can use the `useContent` hook to call it as per other content sources. For this case, you must pass the `imageURL` from the custom fields to the `id` query param of the content source call.
3. Update the image `src` attribute to construct the image `src` as needed for Resizer v2\. As you are calling the signing service content source to get the `auth` token for Resizer v2, you use `content.hash` as the value for the `auth` query param in the image.  
> **Note:**  
>  
> The entire `imageURL` must be URL-encoded, including the protocol.

The resulting code is as follows:

```js
import React from "react";
import PropTypes from "@arc-fusion/prop-types";
import { RESIZER_URL } from "fusion:environment";
import { useContent } from "fusion:content";

const ManualPromo = ({ customFields = {} }) => {
  const { imageURL, headline } = customFields;
  const content = useContent({
    source: 'signing-service',
    query: imageURL ? { id: imageURL } : null,
  }) || null;

  if (!imageURL || !headline) return null;

  return (
    <article>
      <img src={`${RESIZER_URL}/${imageURL}?auth=${content.hash}&width=300`} alt="" />
      <h2>{headline}</h2>
    </article>
  );
};

ManualPromo.propTypes = {
  customFields: PropTypes.shape({
    imageURL: PropTypes.string,
    headline: PropTypes.string,
  }),
};

ManualPromo.label = "Manual Promo Block";
ManualPromo.icon = "paragraph-bullets";

export default ManualPromo;
```

## Further Reading and Resources

To ensure you’re delivering the best experience and images possible to your end users, other optimizations exist for responsive images and, if needed, art-directed images. Arc XP Blocks v2 handle these optimizations natively.

To learn more about image optimizations, see:

- [Responsive images (web.dev)](https://web.dev/learn/design/responsive-images/)
- [Using responsive images in HTML (mdn web docs)](https://developer.mozilla.org/en-US/docs/Learn/HTML/Multimedia%5Fand%5Fembedding/Responsive%5Fimages)

### Example Resizer v2 URL

```
https://clientsite.com/resizer/v2/person-with-red-ball-ABCDEFGHIJKLMNOPQRSTUVWXY.jpg?auth=40b3b900866998ec98c4a286eef727080a10ac968d5eed7bd4a6a084511db6c1&width=500&height=500&quality=80
```

### Example of an image ANS

```json
{
    "_id": "6UI63OCUJI5AW6IHRW46XWAAPU",
    "additional_properties": {
      ...
    },
    "auth": {
        "1": "40b3b900866998ec98c4a286eef727080a10ac968d5eed7bd4a6a084511db6c1"
    },
    ...
    "seo_filename": "person-with-red-ball",
    "url": "https://cloudfront-us-east-1.images.arcpublishing.com/6UI63OCUJI5AW6IHRW46XWAAPU.jpg",
    "type": "image"
}
```
