Giter VIP home page Giter VIP logo

image-url's Introduction

@sanity/image-url

Quickly generate image urls from Sanity image records.

This helper will by default respect any crops/hotspots specified in the Sanity content provided to it. The most typical use case for this is to give it a sanity image and specify a width, height or both and get a nice, cropped and resized image according to the wishes of the content editor and the specifications of the front end developer.

In addition to the core use case, this library provides a handy builder to access the rich selection of processing options available in the Sanity image pipeline.

Getting started

npm install --save @sanity/image-url

Usage

The most common way to use this library in your project is to configure it by passing it your configured sanityClient. That way it will automatically be preconfigured to your current project and dataset:

import React from 'react'
import myConfiguredSanityClient from './sanityClient'
import imageUrlBuilder from '@sanity/image-url'

const builder = imageUrlBuilder(myConfiguredSanityClient)

function urlFor(source) {
  return builder.image(source)
}

Then you can use the handy builder syntax to generate your urls:

<img src={urlFor(author.image).width(200).url()} />

This will ensure that the author image is alway 200 pixels wide, automatically applying any crop specified by the editor and cropping towards the hot-spot she drew. You can specify both width and height like this:

<img src={urlFor(movie.poster).width(500).height(300).url()}>

There are a large number of useful options you can specify, like e.g. blur:

<img src={urlFor(mysteryPerson.mugshot).width(200).height(200).blur(50).url()}>

Note that the url() function needs to be the final one in order to output the url as a string.

Builder methods

image(source)

Specify the image to be rendered. Accepts either a Sanity image record, an asset record, or just the asset id as a string. In order for hotspot/crop processing to be applied, the image record must be supplied, as well as both width and height.

dataset(dataset), projectId(projectId)

Usually you should preconfigure your builder with dataset and project id, but even when you did, these let you temporarily override them if you need to render assets from other projects or datasets.

width(pixels)

Specify the width of the rendered image in pixels.

height(pixels)

Specify the height of the rendered image in pixels.

size(width, height)

Specify width and height in one go.

focalPoint(x, y)

Specify a center point to focus on when cropping the image. Values from 0.0 to 1.0 in fractions of the image dimensions. When specified, overrides any crop or hotspot in the image record.

blur(amount), sharpen(amount), invert()

Apply image processing.

rect(left, top, width, height)

Specify the crop in pixels. Overrides any crop/hotspot in the image record.

format(name)

Specify the image format of the image. 'jpg', 'pjpg', 'png', 'webp'

auto(mode)

Specify transformations to automatically apply based on browser capabilities. Supported values:

  • format - Automatically uses WebP if supported

orientation(angle)

Rotation in degrees. Acceptable values: 0, 90, 180, 270

quality(value)

Compression quality, where applicable. 0-100

forceDownload(defaultFileName)

Make this an url to download the image. Specify the file name that will be suggested to the user.

flipHorizontal(), flipVertical()

Flips the image.

crop(mode)

Specifies how to crop the image. When specified, overrides any crop or hotspot in the image record. See the documentation for details.

fit(value)

Configures the fit mode. See the documentation for details.

dpr(value)

Specifies device pixel ratio scaling factor. From 1 to 3.

saturation(value)

Adjusts the saturation of the image. Currently the only supported value is -100 - meaning it grayscales the image.

ignoreImageParams()

Ignore any specifications from the image record (i.e. crop and hotspot).

url(), toString()

Return the url as a string.

pad(value)

Specify the number of pixels to pad the image.

Deprecated: minWidth(pixels), maxWidth(pixels), minHeight(pixels), maxHeight(pixels)

Specifies min/max dimensions when cropping.

Deprecated: You usually want to use width/height with a fit mode of max or min instead.

Custom CDN domains

ℹ️ This feature is available to select Enterprise accounts. Get in touch with your sales executive to learn more.

You can specify a custom baseUrl in the builder options in order to override the default (https://cdn.sanity.io):

import imageUrlBuilder from '@sanity/image-url'

const builder = imageUrlBuilder({
  baseUrl: 'https://my.custom.domain',
  projectId: 'abc123',
  dataset: 'production',
})
const urlFor = (source) => builder.image(source)

urlFor('image-928ac96d53b0c9049836c86ff25fd3c009039a16-200x200-png')
  .auto('format')
  .fit('max')
  .width(720)
  .toString()

// output: https://my.custom.domain/images/abc123/production/928ac96d53b0c9049836c86ff25fd3c009039a16-200x200.png?auto=format&fit=max&w=720

If you already have a configured client instance:

import imageUrlBuilder from '@sanity/image-url'
import myConfiguredClient from './mySanityClient'

const builder = imageUrlBuilder({
  ...myConfiguredClient.config(),
  baseUrl: 'https://my.custom.domain',
})

License

MIT © Sanity.io

image-url's People

Contributors

bjoerge avatar dependabot[bot] avatar enigosi avatar geball avatar jorngeorg avatar mariuslundgard avatar mmgj avatar readeral avatar rexxars avatar saasen avatar seananagnoste avatar sgulseth avatar simen avatar stufinn avatar tomhah avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

image-url's Issues

Using the `baseUrl` config parameter results in a Typescript error

The configuration parameter is documented here but one can't use it without incurring the following type-error:

Argument of type '{ baseUrl: string; }' is not assignable to parameter of type 'SanityClientLike | SanityProjectDetails | SanityModernClientLike | undefined'.
Object literal may only specify known properties, and 'baseUrl' does not exist in type 'SanityClientLike | SanityProjectDetails | SanityModernClientLike'.(2345)

Code to reproduce:

import createImageUrlBuilder from "@sanity/image-url";

export const imageUrlBuilder = createImageUrlBuilder({
  baseUrl: "https://anything.com", // <-- Type error!
  projectId: "foo",
  dataset: "bar",
});

Here is a link to a minimal reproducible case in the Typescript playground:
https://www.typescriptlang.org/play?#code/JYWwDg9gTgLgBAYygUwIY2QSRKg5sgVSgBsAhAV2GIBNko4AzKCEOAIgAEBnVAO2BgBPAPSg8yALTkSbANwAoecgAekWIgi8u8MfiJlKNOnAC8iFOiw49JClVpQAFAG95cOACNUXQiQBc7MI8-EISCNS8bAA08gC+AJSyQA

Can't set only image height

I expect setting height to work like this:

<img
  src={urlFor(author.image)
    .height(200)
    .url()}
/>

Nothing is happening the image size. ("@sanity/image-url": "^0.140.17")

Export the ImageUrlBuilder type

Wanted to accept an ImageUrlBuilder as a prop in a component, but the type isn't exported, as far as I can see. 😟

Like, I can "see" it via my IDE, but since it's not exported, I can't actually use it as a prop or function parameter type. 😕

Please export the ImageUrlBuilder type?

Implement Saturation option

Sorry I should have raised an issue first. It would be nice to have saturation in the URL builder, rather than manually appending it to the end of the URL string manually.

I've opened a pull request, but you might prefer wait until the saturation option is fully implemented before merging.

Can @sanity/image-url be used in CommonJs ?

Looking to get the absolute url while processing on the server side. Would like to check with the team if we have support available for the same.

const { createImageUrlBuilder } = require('@sanity/image-url');

Specifying both `width` and `height` incorrectly applies `rect`

This library incorrectly sets the rect parameter when both width and height are specified. This prevents many fit modes from working properly. An in-depth visual comparison and breakdown of this bug is available in this CodeSandbox project. At a glance, this screenshot from the linked breakdown demonstrates the issue:

Screen Shot 2021-02-12 at 4 25 13 PM

I considered stripping the rect prop from the returned urls, but it still needs to be present in two scenarios:

  1. When a source has an associated crop
    The rationale here should be self-evident: rect is used to apply crop values.
  2. When the fit mode is min and the source has a focal point
    This library transforms hotspot values into focal point parameters fp-x and fp-y, but due to a bug/oversight in the Images API, these parameters are only used when the fit mode is set to crop. Since the min mode is identical to crop except that the image will not be scaled up, the focal point needs to be emulated via rect (or the bug in the Images API needs to be fixed)

I believe this library should only add a rect parameter in these two scenarios, and to try to pre-compute and apply the focal point in the second case.

Return width and/or height from image url spec

We would like to render images with a maximum width and/or height, and set the width and height attributes on the resulting <img> tag. An easier example would be this:

Image asset with id:
image-38704787082d878c983f9327082be0033ae295bb-698x1056-png

We would like to render that image at a width of 200px, but we don't know the exact height of the image. So we end up with an <img src="..." width="200" /> and without a height set.

Another example would be using the same image, and requesting a w=200&h=150. The resulting image has a width of 99 and a height of 150, but we would be wrong to generate an element like this:

<img src="...image-38704787082d878c983f9327082be0033ae295bb-698x1056-png?w=200&h=150&fit=fill" width="200" height="150" />

We could of course parse the asset ID to find the original image size, and/or use the image metadata to calculate this, but that does not feel scalable as any crop options might apply. All of the logic we would need for that is already being done in the urlForImage() function.

Describe the solution you'd like
We would like a spec method (or equivalent) on imageBuilder to retrieve the calculated specifications that contains the calculated width, height and/or aspect ratio, if it was possible to calculate it.

Describe alternatives you've considered

  • Duplicating the logic in imageBuilder to calculate the same
  • Parsing the resulting URL for information

Additional context
For proper page load performance it is important to set width and height explicitly on <img> elements.

Error: Unable to resolve image URL from source (null)

I recently updated all my packages and now receiving the error above. I can add a conditional to get around the issue but this seems like overkill...

Package.json

....
"dependencies": {
    "@headlessui/react": "^1.5.0",
    "@heroicons/react": "^1.0.6",
    "@sanity/client": "^3.2.0",
    "@sanity/image-url": "^1.0.1",
    "autoprefixer": "^10.4.2",
    "axios": "^0.26.0",
    "clsx": "^1.1.1",
    "heroicons-react": "^1.4.1",
    "immer": "^9.0.12",
    "next": "latest",
    "next-redux-wrapper": "^7.0.5",
    "next-sanity": "^0.5.1",
    "react": "^17.0.2",
    "react-dom": "^17.0.2",
    "react-hook-form": "^7.27.1",
    "react-portable-text": "^0.4.3",
    "react-redux": "^7.2.6",
    "redux": "^4.1.2",
    "redux-devtools-extension": "^2.13.9",
    "redux-saga": "^1.1.3",
    "reselect": "^4.1.5"
  },
  "devDependencies": {
    "@reduxjs/toolkit": "^1.8.0",
    "@tailwindcss/forms": "^0.5.0",
    "@types/node": "17.0.21",
    "@types/react": "17.0.39",
    "@typescript-eslint/eslint-plugin": "^5.13.0",
    "eslint": "8.10.0",
    "eslint-config-next": "12.1.0",
    "eslint-config-prettier": "^8.5.0",
    "husky": "^7.0.4",
    "lint-staged": "^12.3.5",
    "postcss": "^8.4.7",
    "prettier": "^2.5.1",
    "prettier-plugin-tailwindcss": "^0.1.8",
    "tailwindcss": "^3.0.23",
    "typescript": "4.6.2"
  }
}

sanity.js

import {
  createCurrentUserHook,
  createClient,
} from 'next-sanity';
import createImageUrlBuilder from '@sanity/image-url'

export const config = {
  /**
   * Find your project ID and dataset in `sanity.json` in your studio project.
   * These are considered “public”, but you can use environment variables
   * if you want differ between local dev and production.
   *
   * https://nextjs.org/docs/basic-features/environment-variables
   **/
  dataset: process.env.NEXT_PUBLIC_SANITY_DATASET || 'production',
  projectId: process.env.NEXT_PUBLIC_SANITY_PROJECT_ID,
  apiVersion: '2021-08-11', // or today's date for latest
  /**
   * Set useCdn to `false` if your application require the freshest possible
   * data always (potentially slightly slower and a bit more expensive).
   * Authenticated request (like preview) will always bypass the CDN
   **/
  useCdn: process.env.NODE_ENV === 'production',
};

export const sanityClient = createClient(config);

/**
 * Set up a helper function for generating Image URLs with only the asset reference data in your documents.
 * Read more: https://www.sanity.io/docs/image-url
 **/

const builder = createImageUrlBuilder(config)
export const urlFor = (source) => builder.image(source)

// Helper function for using the current logged in user account
export const useCurrentUser = createCurrentUserHook(config);

.tsx

<Image
              className="h-10 w-10 rounded-full"
              src={urlFor(post.author.image).url()} //<-----post.author.image && post.author.image - works but doing this for every image seems like overkill
              alt=""
            />

Image url still comes with default query strings of q and w even if set with the builder

If you find a security vulnerability, do NOT open an issue. Email [email protected] instead.

Describe the bug

When i return the url of an image asset, it comes with default transformation queries of q for quality set to 75 and w for width set to 640. i want to have maximum quality and the default width. When i set these parameters using the urlBuilder, the url returned has both of the above parameters duplicated:
https://cdn.sanity.io/images/yncg0snw/production/74e02c91401ae000160f4497161b3c959dc80e01-777x472.jpg?q=100?w=640&q=75

This leads to an error: {"statusCode":400,"error":"Bad Request","message":"Parameter \"q\" must be a valid integer"}

Expected behavior

I expected the quality query string to be replaced with set value but instead both of them are returned

Which versions of Sanity are you using?

@sanity/cli (global) 3.21.0 (latest: 3.21.3)

What operating system are you using?

MacOs Silicon chip

Which versions of Node.js / npm are you running?

10.2.3
v21.2.0

Typedefs not published on npm

I'm not seeing the type definitions installed with this package. Based on the package.json I'd expect them in lib/. This is what I see.

❯ ls -l
total 136
-rw-r--r--  1 mshick  staff   8603 Oct 26  1985 builder.js
-rw-r--r--  1 mshick  staff   9099 Oct 26  1985 builder.js.map
-rw-r--r--  1 mshick  staff   1877 Oct 26  1985 parseAssetId.js
-rw-r--r--  1 mshick  staff   1699 Oct 26  1985 parseAssetId.js.map
-rw-r--r--  1 mshick  staff   2525 Oct 26  1985 parseSource.js
-rw-r--r--  1 mshick  staff   4595 Oct 26  1985 parseSource.js.map
-rw-r--r--  1 mshick  staff   7695 Oct 26  1985 urlForImage.js
-rw-r--r--  1 mshick  staff  13186 Oct 26  1985 urlForImage.js.map

  ...@sanity/image-url/lib/ 

image is not being cropped at right area

Hello Sanity Team ,

I have the following issue:

At the documentation says that the crop and hotspot values are respected when using

const originalUrl = builder
    .image(rawImgData.asset._id)
    .width(width)
    .height(height)
    .url()

but when I get the image it does not get cropped on at the right position, it does not respect the hotspot or the crop settings.

it generates this rect values
rect=0,612,4898,2041&w=1920&h=800

Also the values from sanity and gatsby are different!

graphql values from sanity deployment

  "hotspot": {
          "x": 0.5,
          "y": 0.6275135628062258,
          "height": 0.7449728743875486,
          "width": 1
        },
        "crop": {
          "top": 0.2550271256124515,
          "bottom": 0,
          "left": 0,
          "right": 0
        }

gatsby source values

hotspot:{
height: 0.6032911379361867
width: 0.8109965635738828
x: 0.5
y: 0.6210734838766183
}

crop{
left: 0
right: 0
top: 0
}

GROQ values

{
  "_type": "sanity.imageCrop",
  "bottom": 0,
  "left": 0,
  "right": 0,
  "top": 0.2550271256124515
}
{
  "_type": "sanity.imageHotspot",
  "height": 0.7449728743875486,
  "width": 1,
  "x": 0.5,
  "y": 0.6275135628062258
}

I think there is a bug, or maybe I'm doing something wrong.
Let me know if you need more details

Thanks!
Miguel Angel

how to use without a sanity client (for example, with gatsby-source-sanity)

When using gatsby-source-sanity, I don't typically have access to a sanityClient directly, but I do have access to a sanity image asset object via the graphql api that the gatsby-source-sanity exposes.

The builder in this example requires a configured sanity client, is there a way to get the convenience of using the builder methods, by passing in a raw sanity asset that was obtained in a gatsby build?

const builder = imageUrlBuilder(myConfiguredSanityClient)

Type 'ImageUrlBuilder' is not assignable to type 'string'

Well, hello there. I'm creating a blog for myself and I'm having trouble typing the value I receive from ImageUrlBuilder on Vue 3 with Composition API and TypeScript. See below for a simplified example.

<template>
  <div>
    <div v-if="post" class="content">
      <h1>{{ post.title }}</h1>
      <img v-if="post.image" :src="imageUrlFor(post.image).width(480)" />
    </div>
  </div>
</template>

<script setup lang="ts">
import imageUrlBuilder from "@sanity/image-url";
import { ref } from "vue";
import type { Ref } from "vue";
import { useRoute } from "vue-router";

import sanity from "../client";

const imageBuilder = imageUrlBuilder(sanity);

const loading = ref(false);
const post: Ref<Record<string, any> | null> = ref(null);

const query = `*[slug.current == $slug] {
  _id,
  title,
  slug,
 "image": mainImage{
    asset->{
      _id,
      url
    }
  }
}[0]
`;

function imageUrlFor(source: any) {
  return imageBuilder.image(source);
}

function fetchData() {
  const route = useRoute();
  loading.value = true;
  sanity.fetch(query, { slug: route.params.slug }).then(
    (data) => {
      loading.value = false;
      post.value = data;
    },
    (err) => {
      console.error(error);
      loading.value = false;
    }
  );
}

fetchData();
</script>

The issue is the one you can read from the title. The img tag's :src attribute is complaning that the return type of ImageUrlBuilder is not a string (as it is namely an ImageUrlBuilder, i.e. an Object). The complete error message is as follows:

(property) ImgHTMLAttributes.src?: string | undefined
Type 'ImageUrlBuilder' is not assignable to type 'string'.ts(2322)
runtime-dom.d.ts(612, 3): The expected type comes from property 'src' which is declared here on type 'ElementAttrs<ImgHTMLAttributes>'

I don't understand how it technically achieves a working image (which it does, I can confirm it works) even though the attribute expects a string. When I console.log it it probably hasn't actually returned a string yet? Is this why also it cannot be set as string? I've read though the presenting images documentation but I'm still perplexed on how to fix this little issue.

ImageUrlBuilder should ignore transformation on gif/svg assets by default

Per https://www.sanity.io/docs/assets#644f57e96542

GIF and SVG pass through the image pipeline untouched, but if you want them scaled they will need to be output as one of the three formats above.

cdn.sanity.io's handling of auto=format on gif images makes no sense and is a foot-gun. Even if it decides one of the three supported formats, the most common use-case for a gif is an animated gif, which precludes png and jpg formats most of the time. This leads to webp being the only potential format for this image type that makes any sense, but when it is chosen, the transformation only outputs the first frame of an animated gif, discarding the rest. This is a seriously unexpected behavior for most people. It's understandable though, because it's extremely expensive to transform an animated gif to webp, but not an acceptable behavior nevertheless.

Note that both gif and svg formats support animation, so transformations in general to them are dangerous.

This package is an accomplice to this bad behavior. Because ImageUrlBuilder is often used in where not much context is known other than "here is an imageObject", such as a general image type renderer used with @sanity/block-content-to-react, if the user has code to generate a URL add .auto('format') their ImageUrlBuilder object inside their image renderer, there is no nice way of avoiding this cdn trap without doing hideous inspection of the imageObject.asset._ref (or the dereferenced image imageObject.asset). Note that other transformation functions may inflict the same problem-- animated images no longer animating. Just in my specific case, I found &auto=format always breaks animated gifs.

By default, when a passed in object to imageObject is a gif (or svg image), it should ignore all image transformations because it risks breaking the potentially animated image unintentionally. I imagine some heuristics can be smarter than a blanket ban, e.g. a svg/gif with &format=jpg which could be a valid transformation, but it increases mental load for developers, so I'd prefer it to be gated so I know I'm about to do something dangerous instead of firing the CS-ticket gun at me later unexpectedly.

With that said...

React lets you do potentially broken things with things like dangerouslySetInnerHTML. This library should do the same for gif/svg files. If they want to transform gif/svg assets, it's probably wise to make it a opt-in behavior where they need to.dangerouslyTransform('gif', 'svg') before any transformation option will be used for blacklisted file types (silently ignore in production, warn once in development mode). This prevents the foot-gun from affecting most users, and respecting existing documentation that states that gifs won't be affected, while keeping their code easy to use. And those who do need transform gif/svg images, it serves as a warning for all other developers looking at the code: "danger, here be dragons".

I noticed this because my ImageUrlBuilder wrapping function that is used heavily throughout my application got inconsistent image type results (sometimes gif, sometimes webp), where any time a webp result was loaded, the animated gif would not animate. This was especially visible for <= iOS 14.4 and Safari 14 user agents. Safari 14 supports webp, which brought this issue to our attention.

Of course, if the cdn is fixed so transforming gifs/svgs return properly animated webp images that has the animations applied, it would be acceptable. Being able to do that on the fly seems iffy though because video transforms are slow and would be a horror show if someone decided to DoS by mass requesting with random transformation parameters (if there aren't already some protection in place to 429 them). A good use case for a 303 response though with the first frame of an animated image though until the animated image is fully processed.

Nest.js usage results in TypeError: (0 , image_url_1.default) is not a function

If you find a security vulnerability, do NOT open an issue. Email [email protected] instead.

Describe the bug

Usage in Nest.js with pnpm results in error:

[Nest] 4705  - 10/10/2023, 11:29:34 PM   ERROR [ExceptionsHandler] (0 , image_url_1.default) is not a function
TypeError: (0 , image_url_1.default) is not a function
    at AppService.getHello (/Users/stephen/dev/sanity-image-url-bug/src/app.service.ts:13:27)
    at AppController.getHello (/Users/stephen/dev/sanity-image-url-bug/src/app.controller.ts:10:28)
    at /Users/stephen/dev/sanity-image-url-bug/node_modules/.pnpm/@[email protected]_@[email protected]_@[email protected][email protected][email protected]/node_modules/@nestjs/core/router/router-execution-context.js:38:29
    at InterceptorsConsumer.intercept (/Users/stephen/dev/sanity-image-url-bug/node_modules/.pnpm/@[email protected]_@[email protected]_@[email protected][email protected][email protected]/node_modules/@nestjs/core/interceptors/interceptors-consumer.js:12:20)
    at /Users/stephen/dev/sanity-image-url-bug/node_modules/.pnpm/@[email protected]_@[email protected]_@[email protected][email protected][email protected]/node_modules/@nestjs/core/router/router-execution-context.js:46:60
    at /Users/stephen/dev/sanity-image-url-bug/node_modules/.pnpm/@[email protected]_@[email protected]_@[email protected][email protected][email protected]/node_modules/@nestjs/core/router/router-proxy.js:9:23
    at Layer.handle [as handle_request] (/Users/stephen/dev/sanity-image-url-bug/node_modules/.pnpm/[email protected]/node_modules/express/lib/router/layer.js:95:5)
    at next (/Users/stephen/dev/sanity-image-url-bug/node_modules/.pnpm/[email protected]/node_modules/express/lib/router/route.js:144:13)
    at Route.dispatch (/Users/stephen/dev/sanity-image-url-bug/node_modules/.pnpm/[email protected]/node_modules/express/lib/router/route.js:114:3)
    at Layer.handle [as handle_request] (/Users/stephen/dev/sanity-image-url-bug/node_modules/.pnpm/[email protected]/node_modules/express/lib/router/layer.js:95:5)

To Reproduce

Steps to reproduce the behavior:

Reproducable in this repo of a new Nest.js project: https://github.com/stephenhmarsh/sanity-image-url-bug

  • clone
  • pnpm install
  • pnpm start
  • navigate to / of the server (localhost:3000)

Expected behavior

The default export of the package should be a function that works.

Screenshots
If applicable, add screenshots to help explain your problem.

Which versions of Sanity are you using?

@sanity/cli (global)  3.15.1 (latest: 3.18.0)
@sanity/image-url      1.0.2 (up to date)

What operating system are you using?

MacOS Sonoma 14.0 (23A344)

Which versions of Node.js / npm are you running?

Run npm -v && node -v in the terminal and copy-paste the result here.

Using pnpm

pnpm -v && node -v
8.9.0
v18.14.0

Additional context

Add any other context about the problem here.

Security issue?

Any security issues should be submitted directly to [email protected]. In order to determine whether you are dealing with a security issue, ask yourself these two questions:

  • Can I access something that's not mine, or something I shouldn't have access to?
  • Can I disable something for other people? If the answer to either of those two questions are "yes", then you're probably dealing with a security issue. Note that even if you answer "no" to both questions, you may still be dealing with a security issue, so if you're unsure, just email us at [[email protected]](mailto:[email protected].

Type error on `imageUrlBuilder`

Describe the bug

Type error

Argument of type 'SanityClient' is not assignable to parameter of type 'SanityClient | SanityProjectDetails'.
  Type 'import("/Users/someone/dev/something-frontend/node_modules/@sanity/client/sanityClient").SanityClient' is not assignable to type 'import("/Users/someone/dev/something-frontend/node_modules/@sanity/image-url/lib/types/types").SanityClient'.
    Types of property 'clientConfig' are incompatible.
      Type 'ClientConfig' is not assignable to type 'SanityProjectDetails & { apiHost: string; }'.
        Type 'ProjectClientConfig' is not assignable to type 'SanityProjectDetails & { apiHost: string; }'.
          Type 'ProjectClientConfig' is not assignable to type 'SanityProjectDetails'.
            Property 'dataset' is optional in type 'ProjectClientConfig' but required in type 'SanityProjectDetails'.

To Reproduce

Steps to reproduce the behaviour:

const sanity = sanityClient({
  projectId: "aaaaaa",
  dataset: "production",
  useCdn: true
});

const imageBuilder = imageUrlBuilder(sanity);

Expected behavior

Should render without any errors

@sanity/cli         1.149.7 (latest: 1.149.9)
@sanity/client      1.149.7 (up to date)
@sanity/image-url  0.140.17 (up to date)

And I'll post an image to demonstrate better what I'm experiencing:

image

image

Add method to include originalFilename (or custom file name) in URL for SEO

Sanity supports preserving an image's original file name (or including a custom file name) by including it after the hashed name, but before, parameters with this format:
https://cdn.sanity.io/images/{project-id}/{dataset}/{hashed-name}/{originalFilename}?params..

Without that, it's the hashed name that gets indexed by search engines, instead of a descriptive file name that could get the image to show up in searches for it.

image-url would become much more useful for projects that need SEOd images if it were easy to include the original file name or a custom file name with a url builder method.

Watermark support

hey! this is probably not the right place for this feature request (since i cannot find the feature in the docs!), but does this library provide support for watermark or embedding/overlaying images on top of images?

for example https://imgix.com/ has "blend" and "mark" where you can reference other images to be placed on top.

image quality degraded when using .width property of the @sanity/image-url npm react package

Description of the bug

When using the code provided by @sanity/image-url npm package for react, giving a custom width to the image degrades it, even when the quality property is set to 100.

To Reproduce

Using
<img src={imageUrlFor(buildImageObj(projectNodes[0].mainImage)) .fit('min') .width(1170) .quality(100) .url()} alt={projectNodes[0].mainImage.alt} />
renders an image with a lowered quality (screenshot of the rendered image):
quality 100

The quality property does work since using it with a parameter of 20 does degrade the quality, as expected:
quality 20

Expected behavior

When resizing the same image with css instead, the image renders with a good quality (screenshot of the rendered image resized with css): (Look at the body text in white to see the difference more easily)
css resized

Original image:
screen3

Sanity version
@sanity/cli 0.147.3 (up to date)

Operating system
Mac OS High Sierra 10.13.6

Node.js / npm versions
6.4.1
v8.12.0

Add .props() as an alternative to .url(), including width and height

Especially when using lazy loading or transition libraries, it's often crucial to set the width and height of an image. With the magic happening with resizing, cropping, focal points, etc., it feels kind of difficult to do that correctly when using this url builder and the Sanity CDN for images.

I suggest you add an alternative to .url(): string, for example .props(): { url: string, width: number, height: number }. One could then just spread those props onto an <img> tag, and if needed also use the width and height values for calculations or whatever.

Note that even if one has only supplied one of height or width, the returned object should always include both, and they should match what will come back from the Sanity CDN.

const imgProps = imageUrlBuilder(config)
    .image(someImageSourceFromSanity)
    .format('jpg')
    .auto('format')
    .width(720)
    .quality(95)
    .props();

console.log('url', imgProps.url);
console.log('width', imgProps.width);
console.log('height', imgProps.height);

return <img {...imgProps} />

Tried looking into it myself, and was kind of hopeful when I found https://github.com/sanity-io/image-url/blob/master/src/urlForImage.ts#L198-L201, but then realized the fit function isn't actually always used, so one would still have to potentially calculate a width and/or height to return in those cases. And that calculation should match whatever is happening in the CDN when its only asked for a width or a height. But haven't been able to find the source for that, so... 😕

Either way, would probably be better if someone in the Sanity team did this, who's a bit more familiar with the code and the backend and everything. 🤔

Issue when webpacking for angular universal

When I use @sanity/image-url in my client project, it works fine

import imageUrlBuilder from '@sanity/image-url';
 constructor() {
            this.builder = imageUrlBuilder({
            projectId: ConfigService.Config.Sanity.ProjectId,
            dataset: ConfigService.Config.Sanity.Dataset
        }); 
    }

and the library used is lib/browser/image-url.umd.js. Then I build for angular ssr, and I use the following webpack config:


module.exports = {
    mode: 'none',
    entry: { server: './server.ts' },
    resolve: { extensions: ['.js', '.ts'], mainFields: [ 'main', 'module'] },
    target: 'node',
    // this makes sure we include node_modules and other 3rd party libraries
    externals: [/(node_modules|main\..*\.js)/],
    output: {
        path: path.join(__dirname, '../garage.host/functions'),
        filename: '[name].js'
    },
    module: {
        rules: [{ test: /\.ts$/, loader: 'ts-loader' }]
    },
    plugins: [
        // Temporary Fix for issue: https://github.com/angular/angular/issues/11580
        // for "WARNING Critical dependency: the request of a dependency is an expression"
        new webpack.ContextReplacementPlugin(
            /(.+)?angular(\\|\/)core(.+)?/,
            path.join(__dirname, 'src'), // location of your src
            {} // a map of your routes
        ),
        new webpack.ContextReplacementPlugin(/(.+)?express(\\|\/)(.+)?/, path.join(__dirname, 'src'), {}),

    ]
};

I use angular-cli to build, now the code used on server render is lib/node/index.js

This errors out with:
ERROR TypeError: image_url_1.default is not a function

At this line

 this.builder = image_url_1.default({
            projectId: services_1.ConfigService.Config.Sanity.ProjectId,
            dataset: services_1.ConfigService.Config.Sanity.Dataset
        });

When I remove ".default" it works.

Is this a problem with webpack or some bug? or is it an angular specific issue? can I do something to make the umd bundle instead of the node lib?

Edit more information: the client bundle is created while tsconifg.compilerOptions.module is set to "esnext", the server is created with "commonjs"

SanityImageSource type is allowing almost everything

type SanityImageSource = string | SanityReference | SanityAsset | SanityImageObject | SanityImageWithAssetStub;

where

interface SanityAsset {
    _id?: string;
    url?: string;
    path?: string;
    assetId?: string;
    extension?: string;
    [key: string]: any;
}

This type will allow almost everything without giving you build errors.

Bug / feature request: Allow desimal in width/height in imagebuilder

I had a problem showing pictures on some devices because I sent a number with decimal to width. It was so hard to debug because I couldn't reproduce the error anywhere. This was my code:

imageUrlBuilder(sanityClient)
.image(this.state.sanity.mainImage)
.width(PixelRatio.getPixelSizeForLayoutSize(width))
.height(PixelRatio.getPixelSizeForLayoutSize(300))
.fit('crop')
.url();

It would be awesome if it did a Math.round(with) or something like that in the library.. So hard to debug if you get the error

My solution:

imageUrlBuilder(sanityClient)
.image(this.state.sanity.mainImage)
.width(Math.round(PixelRatio.getPixelSizeForLayoutSize(width)))
.height(Math.round(PixelRatio.getPixelSizeForLayoutSize(300)))
.fit('crop')
.url();

Gif image not being transformed

I have a gif image and it's not being transformed by Sanity: https://cdn.sanity.io/images/n2jhvipv/production/17cb02d8328fa43018176a871a175cbcacc61b98-1650x950.gif?w=600&h=600&fit=crop&crop=center&auto=format

Untransformed image

I expected the image to be 600x600 px and cropped, but it is not.

On the same website, other images that are jpg are working though: https://cdn.sanity.io/images/n2jhvipv/production/cb0f7e793eeb44e9ce010a7a3efe8731bc444e0a-601x600.jpg?w=600&h=600&fit=crop&crop=center&auto=format

Transformed image

I wonder, is this by design? I saw this issue but wasn't sure what was the change you have implemented: #35

How can I make the transformation work for gif images too?

v1 regression for uploaded images

I have a block with a custom content type for inline images with caption. When using v0.140.22 the component is able to render previews for images I just uploaded from my file-system. On the other hand, v1 breaks execution when doing the same thing. Selecting images from the library works for both versions as expected.

Server and Client urls don't match

I'm using createImageUrlBuilder from next-sanity

// utils/sanity.js

const config = {
  dataset: process.env.SANITY_STUDIO_API_DATASET,
  projectId: process.env.SANITY_STUDIO_API_PROJECT_ID,
}

export const urlFor = source => createImageUrlBuilder(config).image(source)

// components/Component.js

import { urlFor } from 'utils/sanity'

const Component = ({ data }) => {
  if (!data)
    return null
  
  return (
    <div style={{backgroundImage: `url(${urlFor(data.image).url()})`}}></div>
  )
}

export default Component

and I'm getting the following error in the browser console

Warning: Prop `style` did not match. 
Server: "background-image:url(https://cdn.sanity.io/images/myProjectId/staging/43ba14c69f28b68478f5badebaa849e6038b494f-512x512.png?w=400)" 
Client: "background-image:url(https://cdn.sanity.io/images/undefined/undefined/43ba14c69f28b68478f5badebaa849e6038b494f-512x512.png?w=400)"

The image renders, but I'm uncertain as to why this error is logging. Any advice? Thank you!

Type error on imageUrlBuilder

This issue was already stated in 2020 by @pedroapfilho [sanity-io/sanity/issues/1803]

When creating the sanityClient and using ImageBuilder(config) it gives a warn about an error concerning type SanityProjectDetails.

This issue was simply solved by applying /* @ts-ignore */ over that expression. I would suggest just to take care of this issue if possible lol.- Thanks.

by running sanity versions: 
@sanity/cli        2.35.0 (up to date)
@sanity/image-url   1.0.1 (up to date)

next171

next170

Hotspot & Crop does not work

Although I have cropped my image in sanity, in NextJS 13 it does not show up as cropped, but at it's original size

const builder = imageUrlBuilder(client);

function urlFor(source: any) {
    return builder.image(source)
}

<img
src={urlFor(year.portrait)
.width(year.portrait.asset.metadata.dimensions.width)
.height(year.portrait.asset.metadata.dimensions.height)
.url()}
alt=''
/>

Missing pad helper method on ImageUrlBuilder

Hi Sanity,



I noticed the pad helper method is missing on the ImageUrlBuilder class. This is a small change but would make it easier to set the pad parameter when requesting images. It would work similar to height, width, etc...

I'll submit a PR for this soon.


Thanks!

It includes faulty rect parameter

Why does the imageUrlBuilder add the faulty rect parameter to the url?

Example

import createImageUrlBuilder from '@sanity/image-url'

const imageUrlBuilder = createImageUrlBuilder({ projectId, dataset })
const url = imageUrlBuilder.image(source).width(300).height(50).fit('clip').url()

console.log(url)

url outputs https://cdn.sanity.io/images/XXXXXXXX/XXX/filename.png?rect=0,75,1200,200&w=300&h=50&fit=clip. I expected it to be https://cdn.sanity.io/images/XXXXXXXX/XXX/filename.png?w=300&h=50&fit=clip.

This must be a bug? Is there any other way to get the image url and an image constrained within the given width and height without cropping or distorting the image?

imageUrlBuilder baseUrl does not exist in type

Describe the bug
When using baseUrl with imageUrlBuilder i see a type definition warning telling me the baseUrl type does not exists.
When using baseUrl my image URL isn't constructed correctly and I see the broken image link in the browser.

Reproduction repo here: netlify-astro-sanity-portable-text

To Reproduce
Here's my config:

import imageUrlBuilder from '@sanity/image-url';

const builder = imageUrlBuilder({
  baseUrl: import.meta.env.PUBLIC_SITE_URL,
  projectId: import.meta.env.PUBLIC_SANITY_STUDIO_PROJECT_ID,
  dataset: import.meta.env.PUBLIC_SANITY_STUDIO_DATASET,
});

And here's the typescript warning/erorr

> Object literal may only specify known properties, and 'baseUrl' does not exist in type 'SanityClientLike | SanityProjectDetails | SanityModernClientLike'.ts(2353)
(property) baseUrl: any

Expected behavior
This should render an image.

Additional context
If i replace baseUrl with url i still get TypeScript errors, but the image URL is constructed correctly and the image renders in the browser.

E.g

import imageUrlBuilder from '@sanity/image-url';

const builder = imageUrlBuilder({
-  baseUrl: import.meta.env.PUBLIC_SITE_URL,
+  url: import.meta.env.PUBLIC_SITE_URL,
  projectId: import.meta.env.PUBLIC_SANITY_STUDIO_PROJECT_ID,
  dataset: import.meta.env.PUBLIC_SANITY_STUDIO_DATASET,
});

Include metadata such as altText

Is there a way to return metadata such as altText through this package? Or add this functionality?

My current situation:

  • Using sanity-plugin-media, I am able to add alt text to images in the media tab in studio.
  • I query an image reference to render on the frontend. for example -> urlFor(post.image).url()
  • I have to create a separate projection query for the metadata which includes alt text.
  image,
  imageMetadata {
     asset -> {_id, _type, url, altText}
 },

It would be nice if I didn't have to add the metadata in the query. Perhaps add a method like urlFor(post.image).metadata().altText(). Understandable if it's outside the scope of this library.

`SVG` images: using `dpr` alongside `rect` from sanity schema returns incorrectly cropped image

SVGs Only: dpr and rect produce incorrectly cropped image

This may be a duplicate of #32 but I have found this when trying to transform SVGs, which I understand are untouched unless exported as a different format. I'm outputting the SVG as "webp" via .format('webp').

✅ Original Image

You can test this with any SVG using the query parameters in the URL but here is an SVG that is 608 wide and 320 wide:

https://cdn.sanity.io/images/97bpcflt/production/f93de94dc1033f751587b1224f492fec3ff2780d-608x320.svg?&w=608&fm=webp

image


✅ Apply rect

If I apply rect, the image is cropped. Great!
Below, I've added &rect=0,0,608,320 that essentially does nothing.

https://cdn.sanity.io/images/97bpcflt/production/f93de94dc1033f751587b1224f492fec3ff2780d-608x320.svg?&w=608&fm=webp&rect=0,0,608,320

low-res useless crop

To ensure that cropping is actually working, let's update the rect parameter to actually crop the image. Below, I've moved the starting point of the crop to 200 pixels in from the left and the top (rect=200,200,408,120). This isn't necessary, but to keep the same size ratio, I've also subtracted 200 pixels from the width property (w=408).

https://cdn.sanity.io/images/97bpcflt/production/f93de94dc1033f751587b1224f492fec3ff2780d-608x320.svg?&w=408&fm=webp&rect=200,200,408,120

low-res crop


✅ Remove rect, apply dpr

If I remove the rect parameter and append &dpr=2 to the end, I get a scaled version of the original image. Fantastic!

https://cdn.sanity.io/images/97bpcflt/production/f93de94dc1033f751587b1224f492fec3ff2780d-608x320.svg?&w=608&fm=webp&dpr=2

scaled image


🚫 Apply dpr and rect

If I apply any dpr alongside rect the image is jacked up. Below, I've added &dpr=2&rect=0,0,608,320 that should increase the resolution of the image but essentially do no cropping. Alas, it incorrectly crops the image. Ratfarts!

https://cdn.sanity.io/images/97bpcflt/production/f93de94dc1033f751587b1224f492fec3ff2780d-608x320.svg?&w=608&fm=webp&dpr=2&rect=0,0,608,320

high-res broken useless crop

For sanity's sake (pun intended), let's update the rect and width parameters to actually crop the image like we did before (rect=200,200,408,120, w=408).

https://cdn.sanity.io/images/97bpcflt/production/f93de94dc1033f751587b1224f492fec3ff2780d-608x320.svg?&w=408&fm=webp&dpr=2&rect=200,200,408,120

high-res broken crop


This issue does not present itself for png images. I only tested against svg but I suspect it may be present for gif, as well.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.