Skip to content

fix(deps): update dependency @portabletext/react to v6#258

Open
renovate[bot] wants to merge 2 commits into
mainfrom
renovate/portabletext-react-6.x
Open

fix(deps): update dependency @portabletext/react to v6#258
renovate[bot] wants to merge 2 commits into
mainfrom
renovate/portabletext-react-6.x

Conversation

@renovate
Copy link
Copy Markdown
Contributor

@renovate renovate Bot commented Dec 31, 2025

This PR contains the following updates:

Package Change Age Confidence
@portabletext/react ^4.0.3^6.2.0 age confidence

Release Notes

portabletext/react-portabletext (@​portabletext/react)

v6.2.0

Compare Source

Minor Changes
  • #​309 575c9b4 Thanks @​stipsan! - TypeGen-aware Portable Text components

    <PortableText> now infers the shape of every component handler from the value prop. When you pass a value typed by Sanity TypeGen, components.types, components.marks, components.block, components.list, and components.listItem all receive precise value props for the exact content the query returned.

    Three new utility types ship with this feature:

    • InferComponents<T> - same inference as the inline components prop, for hoisting components out of JSX.
    • InferStrictComponents<T> - strict variant that requires a handler for every inferred custom type, mark, block style, and list style, and rejects handlers that aren't in the schema (and therefore not visible to TypeGen).
    • InferValue<T> - derives a Portable Text array value type from any TypeGen query result type, useful for re-usable wrapper components.
Schema

Every example below assumes the same sanity.config.ts:

// sanity.config.ts
import {
  defineArrayMember,
  defineConfig,
  defineField,
  defineType,
} from "sanity";

export default defineConfig({
  name: "default",
  projectId: "abc123",
  dataset: "production",
  schema: {
    types: [
      defineType({
        name: "post",
        type: "document",
        fields: [
          defineField({ name: "title", type: "string" }),
          defineField({
            name: "content",
            type: "array",
            of: [
              defineArrayMember({ type: "block" }),
              defineArrayMember({
                type: "image",
                options: { hotspot: true },
                fields: [defineField({ name: "alt", type: "string" })],
              }),
            ],
          }),
        ],
      }),
    ],
  },
});
Before: hand-typing handlers

Previously, every handler had to be typed by hand to mirror the generated query shape:

// app/[slug]/page.tsx
import { createClient } from "@&#8203;sanity/client";
import { createImageUrlBuilder } from "@&#8203;sanity/image-url";
import { PortableText } from "@&#8203;portabletext/react";
import { defineQuery } from "groq";

const client = createClient({
  projectId: "abc123",
  dataset: "production",
  useCdn: true,
  apiVersion: "2026-05-04",
});
const builder = createImageUrlBuilder(client);

export default async function Page({ slug }: { slug: string }) {
  const query = defineQuery(
    `*[_type == "post" && slug.current == $slug][0]{title,content}`
  );
  const data = await client.fetch(query, { slug });

  if (!data) return notFound();

  return (
    <article>
      <h1>{data.title}</h1>
      <PortableText
        components={{
          types: {
            image: ({
              value,
            }: {
              value: {
                asset?: {
                  _ref: string;
                  _type: "reference";
                  _weak?: boolean;
                };
                hotspot?: {
                  _type: "sanity.imageHotspot";
                  x?: number;
                  y?: number;
                  height?: number;
                  width?: number;
                };
                crop?: {
                  _type: "sanity.imageCrop";
                  top?: number;
                  bottom?: number;
                  left?: number;
                  right?: number;
                };
                alt?: string;
                _type: "image";
                _key: string;
              };
            }) => (
              <img src={builder.image(value).url()} alt={value.alt || ""} />
            ),
          },
        }}
        value={data.content}
      />
    </article>
  );
}
After: automatic inference

Now the same handler is fully typed straight from data.content:

// app/[slug]/page.tsx
import { createClient } from "@&#8203;sanity/client";
import { createImageUrlBuilder } from "@&#8203;sanity/image-url";
import { PortableText } from "@&#8203;portabletext/react";
import { defineQuery } from "groq";

const client = createClient({
  projectId: "abc123",
  dataset: "production",
  useCdn: true,
  apiVersion: "2026-05-04",
});
const builder = createImageUrlBuilder(client);

export default async function Page({ slug }: { slug: string }) {
  const query = defineQuery(
    `*[_type == "post" && slug.current == $slug][0]{title,content}`
  );
  const data = await client.fetch(query, { slug });

  if (!data) return notFound();

  return (
    <article>
      <h1>{data.title}</h1>
      <PortableText
        components={{
          types: {
            // value is fully typed from the query result, no annotation needed
            image: ({ value }) => (
              <img src={builder.image(value).url()} alt={value.alt || ""} />
            ),
          },
        }}
        value={data.content}
      />
    </article>
  );
}
InferComponents: hoisting components without losing inference

Move the components map out of JSX and keep the same inferred handler types:

// app/[slug]/page.tsx
import { createClient } from "@&#8203;sanity/client";
import { createImageUrlBuilder } from "@&#8203;sanity/image-url";
import { PortableText, type InferComponents } from "@&#8203;portabletext/react";
import { defineQuery } from "groq";

const client = createClient({
  projectId: "abc123",
  dataset: "production",
  useCdn: true,
  apiVersion: "2026-05-04",
});
const builder = createImageUrlBuilder(client);

export default async function Page({ slug }: { slug: string }) {
  const query = defineQuery(
    `*[_type == "post" && slug.current == $slug][0]{title,content}`
  );
  const data = await client.fetch(query, { slug });

  if (!data) return notFound();

  const components = {
    types: {
      image: ({ value }) => (
        <img src={builder.image(value).url()} alt={value.alt || ""} />
      ),
    },
  } satisfies InferComponents<typeof data.content>;

  return (
    <article>
      <h1>{data.title}</h1>
      <PortableText components={components} value={data.content} />
    </article>
  );
}
InferStrictComponents + InferValue: a strict, re-usable wrapper

InferValue<SanityQueries[keyof SanityQueries]> collects every Portable Text item shape from every registered TypeGen query into an array value type, and InferStrictComponents requires a handler for each of them. Together they're perfect for a single CustomPortableText you reuse across the app:

// app/[slug]/page.tsx
import { createClient, type SanityQueries } from "@&#8203;sanity/client";
import { createImageUrlBuilder } from "@&#8203;sanity/image-url";
import {
  PortableText,
  type InferStrictComponents,
  type InferValue,
} from "@&#8203;portabletext/react";
import { defineQuery } from "groq";

const client = createClient({
  projectId: "abc123",
  dataset: "production",
  useCdn: true,
  apiVersion: "2026-05-04",
});
const builder = createImageUrlBuilder(client);

// Array value type for every Portable Text item shape across all registered queries.
type PortableTextValue = InferValue<SanityQueries[keyof SanityQueries]>;

function CustomPortableText({ value }: { value: PortableTextValue }) {
  const components = {
    types: {
      image: ({ value }) => (
        <img src={builder.image(value).url()} alt={value.alt || ""} />
      ),
    },
  } satisfies InferStrictComponents<PortableTextValue>;
  //   ^ TypeScript errors when the schema gains a custom type, mark, or list
  //     style without a matching handler defined here

  return <PortableText components={components} value={value} />;
}

export default async function Page({ slug }: { slug: string }) {
  const query = defineQuery(
    `*[_type == "post" && slug.current == $slug][0]{title,content}`
  );
  const data = await client.fetch(query, { slug });

  if (!data) return notFound();

  return (
    <article>
      <h1>{data.title}</h1>
      {Array.isArray(data.content) && (
        <CustomPortableText value={data.content} />
      )}
    </article>
  );
}

v6.1.0

Compare Source

Minor Changes

v6.0.3

Compare Source

Patch Changes

v6.0.2

Compare Source

Patch Changes

v6.0.1

Compare Source

Patch Changes

v6.0.0

Compare Source

Major Changes
Patch Changes

v5.0.0

Compare Source

Major Changes

Configuration

📅 Schedule: (UTC)

  • Branch creation
    • "before 3am on Monday"
  • Automerge
    • At any time (no schedule defined)

🚦 Automerge: Disabled by config. Please merge this manually once you are satisfied.

Rebasing: Whenever PR is behind base branch, or you tick the rebase/retry checkbox.

🔕 Ignore: Close this PR and you won't be reminded about this update again.


  • If you want to rebase/retry this PR, check this box

This PR has been generated by Mend Renovate using a curated preset maintained by Sanity. View repository job log here

@changeset-bot
Copy link
Copy Markdown

changeset-bot Bot commented Dec 31, 2025

🦋 Changeset detected

Latest commit: f1d06e6

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 1 package
Name Type
@portabletext/react-native Patch

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

@socket-security
Copy link
Copy Markdown

socket-security Bot commented Dec 31, 2025

Review the following changes in direct dependencies. Learn more about Socket for GitHub.

Diff Package Supply Chain
Security
Vulnerability Quality Maintenance License
Added@​portabletext/​react@​6.2.09910010096100

View full report

@renovate renovate Bot force-pushed the renovate/portabletext-react-6.x branch 8 times, most recently from 7b4fe2e to 95739a9 Compare December 31, 2025 15:47
@renovate renovate Bot force-pushed the renovate/portabletext-react-6.x branch from 04b8ae1 to 24819f5 Compare January 8, 2026 18:28
@renovate renovate Bot force-pushed the renovate/portabletext-react-6.x branch 2 times, most recently from bfad650 to 6d45e64 Compare January 23, 2026 16:30
@renovate renovate Bot force-pushed the renovate/portabletext-react-6.x branch from 23965c6 to d081846 Compare February 2, 2026 18:29
@renovate renovate Bot force-pushed the renovate/portabletext-react-6.x branch 2 times, most recently from cc8e5cb to df1a852 Compare February 17, 2026 16:58
@renovate renovate Bot force-pushed the renovate/portabletext-react-6.x branch 3 times, most recently from dc4da42 to 3bb70b8 Compare March 5, 2026 17:59
@renovate renovate Bot force-pushed the renovate/portabletext-react-6.x branch from b402ea0 to bfd0ad3 Compare March 13, 2026 18:42
@renovate renovate Bot force-pushed the renovate/portabletext-react-6.x branch from 423440d to bd4e1a5 Compare April 1, 2026 17:53
@renovate renovate Bot force-pushed the renovate/portabletext-react-6.x branch from 587d242 to 230721f Compare April 8, 2026 20:14
@renovate renovate Bot force-pushed the renovate/portabletext-react-6.x branch 2 times, most recently from e1aa7e6 to beb057b Compare May 5, 2026 10:55
@renovate renovate Bot force-pushed the renovate/portabletext-react-6.x branch 2 times, most recently from 7e6225c to 7b20458 Compare May 12, 2026 11:46
@renovate renovate Bot force-pushed the renovate/portabletext-react-6.x branch from 978a0b6 to c7d0235 Compare May 18, 2026 17:56
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

0 participants