Skip to content

Commit

Permalink
feat: Remove Default 2-Stage Image Sizing (#431)
Browse files Browse the repository at this point in the history
# Description

This removes the 2-stage image sizing that occurs on URLs generated via
the image component and helpers to avoid producing lower resolution
images that necessary for images with a larger original source.

Instead, this primarily provides a post-transformation resize mechanism
and a new `crop` API that provides the ability to customize how crops
are applied.

In an original example of:
```
<CldImage width height crop="fill" ... />
```

The resulting URL may look like:

```
/w_width,h_height,c_fill/transformations/w_width,c_limit/
```

This would always restrict the resulting image from being no larger than
the initial width, which is based on the desired render size, not the
original image's source size.

With the new crop API, by using:
```
<CldImage width height crop="fill" ... />
```

The resulting URL may look like:

```
transformations/w_width,h_height,c_fill/
```

And to replicate the original 2-stage effect, you can do:

```
<CldImage width height crop={{ type: 'fill', source: true }} ... />
```

Where the resulting URL may look like:

```
/w_width,h_height,c_fill/transformations/w_width,c_limit/
```

Please see the RFC for more context and reasoning:
#432

BREAKING CHANGE: Changes how cropping fundamentally works, introduces new functionality behind crop prop
  • Loading branch information
colbyfayock authored Feb 22, 2024
1 parent 2bf56c3 commit b8de6f0
Show file tree
Hide file tree
Showing 36 changed files with 7,255 additions and 1,038 deletions.
3 changes: 2 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,8 @@

######

<a href="https://github.com/cloudinary-community/next-cloudinary/actions/workflows/test_and_release.yml"><img alt="GitHub Workflow Status" src="https://img.shields.io/github/actions/workflow/status/cloudinary-community/next-cloudinary/test_and_release.yml?branch=main&label=Test%20%26%20Release&style=flat-square"></a> <a href="https://www.npmjs.com/package/next-cloudinary"><img alt="npm" src="https://img.shields.io/npm/v/next-cloudinary?style=flat-square"></a> <a href="https://github.com/cloudinary-community/next-cloudinary/blob/main/LICENSE"><img alt="GitHub" src="https://img.shields.io/github/license/cloudinary-community/next-cloudinary?label=License&style=flat-square"></a>
<a href="https://github.com/cloudinary-community/next-cloudinary/actions/workflows/test_and_release.yml"><img alt="GitHub Workflow Status" src="https://img.shields.io/github/actions/workflow/status/cloudinary-community/next-cloudinary/test_and_release.yml?branch=main&label=Test%20%26%20Release&style=flat-square"></a> <a href="https://www.npmjs.com/package/next-cloudinary"><img alt="npm" src="https://img.shields.io/npm/v/next-cloudinary?style=flat-square"></a> <a href="https://bundlephobia.com/package/next-cloudinary"><img alt="npm bundle size" src="https://img.shields.io/bundlephobia/min/next-cloudinary?style=flat-square&label=Minified%20Size"></a> <a href="https://github.com/cloudinary-community/next-cloudinary/blob/main/LICENSE"><img alt="GitHub" src="https://img.shields.io/github/license/cloudinary-community/next-cloudinary?label=License&style=flat-square"></a>


# Next Cloudinary

Expand Down
6 changes: 4 additions & 2 deletions docs/components/CodeBlock/CodeBlock.js
Original file line number Diff line number Diff line change
@@ -1,13 +1,15 @@
import { useState } from 'react';
import { BsArrowsExpand, BsArrowsCollapse } from 'react-icons/bs'

import { cn } from '../../lib/utils';

import styles from './CodeBlock.module.scss';

export const CodeBlock = ({ children }) => {
export const CodeBlock = ({ children, className }) => {
const [expanded, setExpanded] = useState(false);

return (
<div className={styles.codeBlock}>
<div className={cn(styles.codeBlock, className)}>
<div className={styles.codeBlockCode} data-codeblock-expanded={expanded}>
{ children }
</div>
Expand Down
1 change: 0 additions & 1 deletion docs/components/CodeBlock/CodeBlock.module.scss
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
.codeBlock {
position: relative;
margin: 1.5em 0;
}

.codeBlockCode {
Expand Down
23 changes: 23 additions & 0 deletions docs/components/ExamplesCldOgImage/ExamplesCldOgImage.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
import { CldImage } from '../../../next-cloudinary/dist';
import { OG_IMAGE_WIDTH, OG_IMAGE_HEIGHT } from '../../../next-cloudinary/src/constants/sizes';

const ExamplesCldOgImage = ({ ...props }) => {
return (
<CldImage
width={OG_IMAGE_WIDTH}
height={OG_IMAGE_HEIGHT}
crop={{
width: OG_IMAGE_WIDTH,
height: OG_IMAGE_HEIGHT,
type: 'fill',
gravity: 'center',
source: true
}}
sizes="100vw"
alt=""
{...props}
/>
)
}

export default ExamplesCldOgImage;
1 change: 1 addition & 0 deletions docs/components/ExamplesCldOgImage/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export { default } from './ExamplesCldOgImage';
7 changes: 7 additions & 0 deletions docs/lib/utils.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
import {clsx } from "clsx"
import { twMerge } from "tailwind-merge"

export function cn(...inputs) {
return twMerge(clsx(inputs))
}

22 changes: 12 additions & 10 deletions docs/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -11,20 +11,22 @@
"dependencies": {
"@cloudinary-util/url-loader": "4.2.0",
"@cloudinary-util/util": "^3.0.0",
"@vercel/analytics": "^1.0.1",
"cloudinary": "^1.37.3",
"next": "^14.0.0",
"@vercel/analytics": "^1.2.2",
"cloudinary": "^2.0.1",
"clsx": "^2.1.0",
"next": "^14.1.0",
"nextjs-google-analytics": "^2.3.3",
"nextra": "^2.13.2",
"nextra-theme-docs": "^2.13.2",
"nextra": "^2.13.3",
"nextra-theme-docs": "^2.13.3",
"react": "^18.2.0",
"react-dom": "^18.2.0",
"react-icons": "^4.11.0",
"sass": "^1.63.6"
"react-icons": "^5.0.1",
"sass": "^1.71.1",
"tailwind-merge": "^2.2.1"
},
"devDependencies": {
"autoprefixer": "^10.4.16",
"postcss": "^8.4.31",
"tailwindcss": "^3.3.5"
"autoprefixer": "^10.4.17",
"postcss": "^8.4.35",
"tailwindcss": "^3.4.1"
}
}
6 changes: 6 additions & 0 deletions docs/pages/_meta.json
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,12 @@
"breadcrumb": false
}
},
"changelog": {
"title": "Changelog",
"theme": {
"breadcrumb": false
}
},
"Components": {
"type": "separator",
"title": "Components"
Expand Down
59 changes: 59 additions & 0 deletions docs/pages/changelog.mdx
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
import Head from 'next/head';
import { Callout, Steps, Tab, Tabs } from 'nextra-theme-docs';


import OgImage from '../components/OgImage';

import { CldImage } from '../../next-cloudinary';
import { OG_IMAGE_WIDTH, OG_IMAGE_HEIGHT } from '../../next-cloudinary/src/constants/sizes';

<Head>
<title>Installation - Next Cloudinary</title>
<meta name="og:title" content="Installing Next Cloudinary - Next Cloudinary" />
<meta name="og:url" content={`https://next.cloudinary.dev/installation`} />
</Head>

<OgImage
title="Changelog"
twitterTitle="Changelog"
/>

# Changelog

## v6.0.0

### Overview

### Changes

* Migrates project to pnpm ([#419](https://github.com/cloudinary-community/next-cloudinary/pull/419))

**CldImage, getCldImageUrl, CldOgImage, getCldOgImageUrl**

<Callout emoji={false} type="warning">
This is a fundamental change in how the API works
</Callout>

* Removes default 2-stage resizing to avoid low resolution images for larger source images ([#431](https://github.com/cloudinary-community/next-cloudinary/pull/431))
* Learn more about [cropping](/cldimage/configuration#crop)
* Learn more about [responsive images](/guides/responsive-images)
* See the RFC with more details behind the change: https://github.com/cloudinary-community/next-cloudinary/discussions/432

**CldImage**
* Deprecates `transformations` in favor of `namedTransformations`

**getCldImageUrl**
* Removes types GetCldImageUrl and GetCldOgImageUrl

**CldUploadWidget**
* Add Content-Type to CldUploadWidget signature endpoint ([#379](https://github.com/cloudinary-community/next-cloudinary/issues/379))
* Deprecates some CldUploadWidget types in favor of natively defined types from [@cloudinary-util/types](https://github.com/colbyfayock/cloudinary-util/tree/main/packages/types)
* CldUploadWidgetInfo, CldUploadWidgetPropsOptions, CldUploadWidgetResults
* Updates onError and onClose callbacks to have a consistent API with the rest of the callbacks ([#424](https://github.com/cloudinary-community/next-cloudinary/pull/424))
* Deprecates onUpload in favor of onSuccess, matching the native Cloudinary Upload Widget API ([#424](https://github.com/cloudinary-community/next-cloudinary/pull/424))

**CldVideoPlayer**
* Removes autoPlay in favor of autoplay
* Video Player: CldVideoPlayerPropsColors
* Deprecates some CldVideoPlayer types in favor of natively defined types from [@cloudinary-util/types](https://github.com/colbyfayock/cloudinary-util/tree/main/packages/types)
* CldVideoPlayerPropsColors
6 changes: 3 additions & 3 deletions docs/pages/cldimage/basic-usage.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -77,7 +77,7 @@ You can further take advantage of Cloudinary features like background removal an
src={`${process.env.IMAGES_DIRECTORY}/turtle`}
crop="fill"
removeBackground
tint="70:blue:green:purple"
tint="70:blue:purple"
underlay={`${process.env.IMAGES_DIRECTORY}/galaxy`}
sizes="100vw"
alt="Turtle in the ocean"
Expand All @@ -94,7 +94,7 @@ You can further take advantage of Cloudinary features like background removal an
src="<Public ID>"
crop="fill"
removeBackground
tint="70:blue:green:purple"
tint="70:blue:purple"
underlay="<Public ID>"
sizes="100vw"
alt="Description of my image"
Expand All @@ -112,7 +112,7 @@ must include a version number (/v1234) in order to be correctly parsed.
to ensure the integretiy when during the parsing process.
</Callout>

<CodeBlock>
<CodeBlock className="mt-6">
```jsx copy showLineNumbers
import { CldImage } from 'next-cloudinary';

Expand Down
98 changes: 96 additions & 2 deletions docs/pages/cldimage/configuration.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -135,9 +135,9 @@ right inside of Next.js.

{
prop: 'crop',
type: 'string',
type: 'string | object',
default: () => (<code>limit</code>),
example: () => (<code>thumb</code>),
example: () => (<code>fill</code>),
more: () => (<a className="whitespace-nowrap" href="https://cloudinary.com/documentation/transformation_reference#c_crop_resize">More Info</a>)
},
{
Expand Down Expand Up @@ -222,12 +222,106 @@ background="blue"

Changes the size of the delivered asset according to the requested width & height dimensions.

The `crop` prop can either be a string, which can accept any [valid Cloudinary crop mode](https://cloudinary.com/documentation/transformation_reference#c_crop_resize),
or it can accept an object or an array of objects which can take the following options:

<Table
columns={[
{
id: 'prop',
title: 'Prop'
},
{
id: 'type',
title: 'Type'
},
{
id: 'example',
title: 'Example'
}
]}
data={[
{
prop: 'aspectRatio',
type: 'string',
example: () => (<code>16:9</code>),
},
{
prop: 'gravity',
type: 'string',
example: () => (<a href="#gravity">See Gravity</a>),
},
{
prop: 'height',
type: 'string',
example: () => (<a href="#required-props">See Height</a>),
},
{
prop: 'source',
type: 'boolean',
example: () => (<code>true</code>),
},
{
prop: 'type (crop mode)',
type: 'string',
example: () => (<code>fill</code>),
},
{
prop: 'width',
type: 'string',
example: () => (<a href="#required-props">See Width</a>),
},
{
prop: 'zoom',
type: 'string',
example: () => (<a href="#zoom">See Zoom</a>),
},
]}
/>

**Dynamic Crop Modes**

When using a dynamic crop mode, such as `thumb`, the resulting image may
be visually different based on the given dimensions. For instance, an
image cropped using the `thumb` crop mode with dimensions 600x600 will
give different results than 1200x1200 (assuming a gravity of auto or similar,
which is the default for CldImage).

This is especially important in the context of [Responsive Images](/guides/responsive-images)
where due to the resize mechanism, different device sizes may result
in different looking images, which doesn't provide a great experience.

To resolve this, when using dynamic crop modes you may want to opt into
a two-stage crop, first cropping the original source image, then allowing
the the resize mechanism to handle resizing that to the appropriate device
size. See examples below.

<Callout emoji={false}>
Versions 5 and below of Next Cloudinary automatically opted CldImage in
to a two-stage cropping to help improve the experience, but it came with
drawbacks including prematurely limiting the potential resulting size
of an image. [Learn more](https://github.com/cloudinary-community/next-cloudinary/discussions/432).
</Callout>

**Examples**

Cropping an image and filling the containing space:

```jsx copy
crop="fill"
```

Using a crop of `thumb` on the original source image:

```jsx copy
crop={{
width: 1200,
height: 1200,
type: 'thumb',
source: true
}}
```

[Learn more about the crop transformation](https://cloudinary.com/documentation/transformation_reference#c_crop_resize) on the Cloudinary docs.

### `fillBackground`
Expand Down
Loading

0 comments on commit b8de6f0

Please sign in to comment.