GithubHelp home page GithubHelp logo

portabletext / react-portabletext Goto Github PK

View Code? Open in Web Editor NEW
286.0 8.0 29.0 4.16 MB

Render Portable Text with React

Home Page: https://portabletext.github.io/react-portabletext/

License: MIT License

TypeScript 99.79% Shell 0.21%
portable-text

react-portabletext's Issues

Issue passing client components on Next.js v14.*

Current setup:

<PortableText
  value={value}
  components={{
    block: {
      h2: (node) => <ClientComponentHeading as="h2" {...node} />,
      h3: (node) => <ClientComponentHeading as="h3" {...node} />,
    },
  }}
/>

Where ClientComponentHeading.tsx is a client component (i.e. 'use client')

Which outputs the following error message in the Next.js console:

⨯ Error: Functions cannot be passed directly to Client Components unless you explicitly expose it by marking it with "use server". Or maybe you meant to call this function rather than return it.
  <... as="h2" children={[...]} index=... isInline=... node=... value=... renderNode={function renderNode}>
                                                                                     ^^^^^^^^^^^^^^^^^^^^^
    at stringify (<anonymous>)
    at stringify (<anonymous>)
    at stringify (<anonymous>)
    at stringify (<anonymous>)
digest: "3429654648"

My question is... how can I pass/use a client component in the <PortableText>?

Making the parent component also a client component does not work as it outputs the following error message:

Error: async/await is not yet supported in Client Components, only Server Components.
This error is often caused by accidentally adding `'use client'` to a module that was originally written for the server.

[Feature Request] Provide a sort mechanism to define render order of marks

Hi,

Currently marks render alphabetically and I'm facing a use case where I need to have a mark render before another.

See this codesandbox.

Issue

I need elements with .border to be rendered as child of .color. Currently border mark renders first because of alphabetically sorting.

.color {
  color: red;
}

.border {
  border: 1px solid currentColor;
}

Proposal

Providing a marksOrder prop to force some marks to render in a specific order.
All other marks will render after.
Using '*' will define the position of all others marks ['*', 'myMark'].

const components = {
  marks: {
    color: ({children}) => <span className="color">{children}</span>,
    border: ({children}) => <span className="border">{children}</span>,
  },
  marksOrder: ['color', 'border'], // equivalent to `['color', 'border', '*']`
  // marksOrder: ['*', 'color', 'border'], // render all before those specifics marks
}

Thanks

Feature request: Tables

It would be nice to have table customization or configuration possible for it.

If someone have a solution for this, please let me know!!

Named export 'LIST_NEST_MODE_HTML' not found.

Hi,

We have a Next JS project that is now failing to build due to the following error:

SyntaxError: Named export 'LIST_NEST_MODE_HTML' not found. The requested module '@portabletext/toolkit' is a CommonJS module

It seems to be related to the recent release, wondering if anyone else is having the issue and if there is a solution / work around?

Our project is a next/sanity build
next 12.3.1
yarn 3.2.1

Thanks Tim

Hydration errors from nested portable text blocks

For a portable text block, I have a serializer in marks to specify rendering a component, and within that component, I'm using another portable text block. By default, the topmost div gets wrapped in a <p> tag, causing hydration errors due to that topmost <p> tag having children with <div> and <p> in it.

So, is there a way to render the block without a <p> tag automatically wrapping the component, and thus avoid hydration errors?


Here's a simple outline of the code for reference:

const MyApp = () => {
  return <PortableText value={body} components={PortableTextComponents} />
}

const PortableTextComponents = {
  marks: {
    serializer: ({value}) => {
      return <MyComponent body={value} />
    },
  },
}

const MyComponent = ({body}) => {
  return <PortableText value={body} />
}

Anchored Headings for Portable Text

Wondering how we would do something similar to this...

https://www.sanity.io/schemas/anchored-headings-for-portable-text-a6c70b6e - created by @kmelve

I think I understand how to create the new block definitions for h1, h2 etc..., or a complete handler like in the example above, but in the code below, I don't believe node is available from props? And I'm not sure what to return instead of return PortableText.defaultSerializers.types.block(props) (since defaultSerializers is no longer on PortableText)

const components: PortableTextComponents = {
  block: props => {
    const { node, children } = props
    const { style, _key } = node

    if (/^h\d/.test(style)) {
      const HeadingTag = style
      // Even though HTML5 allows id to start with a digit, we append it with a letter to avoid various JS methods to act up and make problems
      const headingId = `h${_key}`
      return (
        <HeadingTag id={headingId}>
          <a href={`#${headingId}`} aria-hidden="true" tabIndex={-1}>
            #
          </a>
          <span>{children}</span>
        </HeadingTag>
      )
    }
    // ... you can put in other overrides here

    // or return the default ones 👇
    return PortableText.defaultSerializers.types.block(props)
  }
}

Any thoughts or suggestions greatly appreciated.

Type Guard

react-portable-text.tsx markKey

I'm having an issue with the types for markKey as it's expecting a string but the type could be a string or undefined.
@portabletext/react/src/react-portable-text.tsx line 180

Screenshot 2022-11-20 at 20 24 42

Now if the types are right then we need a way to deal with it possibly being undefined and type-guarded. Or maybe it's already type guided but we are not inferring the type after?

  function renderSpan(node: ToolkitNestedPortableTextSpan, _index: number, key: string) {
    const {markDef, markType, markKey} = node
    const Span = components.marks[markType] || components.unknownMark
    const children = node.children.map((child, childIndex) =>
      renderNode({node: child, index: childIndex, isInline: true, renderNode})
    )

    if (Span === components.unknownMark) {
      handleMissingComponent(unknownMarkWarning(markType), {nodeType: 'mark', type: markType})
    }

  const isDefined  =<T,> (value: T | undefined): value is T => typeof value !== 'undefined' 

  if(isDefined(markKey))
    return (
      <Span
        key={key}
        text={spanToPlainText(node)}
        value={markDef}
        markType={markType}
        markKey={markKey}
        renderNode={renderNode}
      >
        {children}
      </Span>
    )

    return <></>
  }

buildMarksTree.ts

website/node_modules/@portabletext/toolkit/src/buildMarksTree.ts. line 94

_key

Screenshot 2022-11-20 at 20 40 22

markDef

Screenshot 2022-11-20 at 20 43 47

similar issue here with _key, and markDef...

    const isDefined  =<T,> (value: T | undefined): value is T => typeof value !== 'undefined' 

    for (const markKey of marksNeeded) {
      const markDef = markDefs.find((def) => def._key === markKey)
      const markType = markDef ? markDef._type : markKey
      const node: ToolkitNestedPortableTextSpan<M> = {
        _type: '@span',
        children: [],
        markType,
        markKey,
      }

    if(isDefined(span._key)) node._key = span._key
    if(isDefined(markDef)) node.markDef = markDef

My suggestion would be something like this, but you may have better ideas.

TypeScript usage?

I would love to have some docs showing how this is supposed to be used with TypeScript!

Version 2.0.1 breaks backwards compatibility

We are useing Sanity 2.x and the webpack parser can not handle the changes introduced in this minor version.

Error in ./node_modules/@portabletext/react/dist/react-portable-text.esm.js
Module parse failed: Unexpected token (12:4)
You may need an appropriate loader to handle this file type.
|     marks,
|     types,
|     ...rest
|   } = overrides;

This most likely has to do with the rest/spread syntax. With 2.0.0 the code was this:

  const {
      block,
      list,
      listItem,
      marks,
      types
    } = overrides,
    rest = _objectWithoutProperties(overrides, _excluded);
  return _objectSpread(_objectSpread({}, parent), {}, {
    block: mergeDeeply(parent, overrides, "block"),
    list: mergeDeeply(parent, overrides, "list"),
    listItem: mergeDeeply(parent, overrides, "listItem"),
    marks: mergeDeeply(parent, overrides, "marks"),
    types: mergeDeeply(parent, overrides, "types")
  }, rest);

With the new code it is:

  const {
    block,
    list,
    listItem,
    marks,
    types,
    ...rest
  } = overrides;
  return {
    ...parent,
    block: mergeDeeply(parent, overrides, "block"),
    list: mergeDeeply(parent, overrides, "list"),
    listItem: mergeDeeply(parent, overrides, "listItem"),
    marks: mergeDeeply(parent, overrides, "marks"),
    types: mergeDeeply(parent, overrides, "types"),
    ...rest
  };

It seems the introduction of a major version of @sanity/pkg-utils has introduced this change in 32536f6#diff-7ae45ad102eab3b6d7e7896acd08c427a9b25b346470d7bc6507b6481575d519L52

PortableText components prop

Issue

  • component props default state of PortableText is only rendering paragraphs for marks

Raw JSON from Sanity Block

"blockContent": [
  {
    "_key": "1e1e89d5cc17",
    "_type": "block",
    "children": [
      {
        "_key": "169646e523900",
        "_type": "span",
        "marks": [],
        "text": "Block Content"
      }
    ],
    "markDefs": [],
    "style": "h2"
  },
  {
    "_key": "2a0aeea4e378",
    "_type": "block",
    "children": [
      {
        "_key": "1875c0d851d2",
        "_type": "span",
        "marks": [],
        "text": ""
      }
    ],
    "markDefs": [],
    "style": "normal"
  },
  {
    "_key": "f6bd276c19e2",
    "_type": "block",
    "children": [
      {
        "_key": "a07c1c38620a",
        "_type": "span",
        "marks": [],
        "text": "\"Lorem ipsum dolor sit amet, "
      },
      {
        "_key": "555f100a0766",
        "_type": "span",
        "marks": [
          "fb9b93ad297e"
        ],
        "text": "consectetur"
      },
      {
        "_key": "c3c736570215",
        "_type": "span",
        "marks": [],
        "text": " adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore \"magna\" aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. "
      },
      {
        "_key": "6129277bb3a2",
        "_type": "span",
        "marks": [
          "917ed6187abc"
        ],
        "text": "Excepteur sint occaecat"
      },
      {
        "_key": "3245dd0eec72",
        "_type": "span",
        "marks": [],
        "text": " cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.\""
      }
    ],
    "markDefs": [
      {
        "_key": "fb9b93ad297e",
        "_type": "link",
        "href": "https://google.com"
      },
      {
        "_key": "917ed6187abc",
        "_type": "link",
        "href": "#test"
      }
    ],
    "style": "normal"
  },
  {
    "_key": "cc1e07c03566",
    "_type": "block",
    "children": [
      {
        "_key": "9bfc7d0b8c3f",
        "_type": "span",
        "marks": [],
        "text": ""
      }
    ],
    "markDefs": [],
    "style": "normal"
  },
  {
    "_key": "f0c17447acb0",
    "_type": "block",
    "children": [
      {
        "_key": "1759fab50603",
        "_type": "span",
        "marks": [],
        "text": "Item One"
      }
    ],
    "level": 1,
    "listItem": "bullet",
    "markDefs": [],
    "style": "normal"
  },
  {
    "_key": "4197ac03d360",
    "_type": "block",
    "children": [
      {
        "_key": "6afd11704412",
        "_type": "span",
        "marks": [],
        "text": "Item Two"
      }
    ],
    "level": 1,
    "listItem": "bullet",
    "markDefs": [],
    "style": "normal"
  },
  {
    "_key": "2fa0377ac532",
    "_type": "block",
    "children": [
      {
        "_key": "0282e423c109",
        "_type": "span",
        "marks": [],
        "text": "Item Three"
      }
    ],
    "level": 1,
    "listItem": "bullet",
    "markDefs": [],
    "style": "normal"
  },
  {
    "_key": "4866c26a44d5",
    "_type": "block",
    "children": [
      {
        "_key": "05f9b12885e4",
        "_type": "span",
        "marks": [],
        "text": ""
      }
    ],
    "markDefs": [],
    "style": "normal"
  },
  {
    "_key": "10da741b2c38",
    "_type": "block",
    "children": [
      {
        "_key": "aeff8d2c1273",
        "_type": "span",
        "marks": [],
        "text": "Item One"
      }
    ],
    "level": 1,
    "listItem": "number",
    "markDefs": [],
    "style": "normal"
  },
  {
    "_key": "8db903e89207",
    "_type": "block",
    "children": [
      {
        "_key": "b638bffc6db0",
        "_type": "span",
        "marks": [],
        "text": "Item Two"
      }
    ],
    "level": 1,
    "listItem": "number",
    "markDefs": [],
    "style": "normal"
  },
  {
    "_key": "cb957b822bb6",
    "_type": "block",
    "children": [
      {
        "_key": "a6ffd8cef967",
        "_type": "span",
        "marks": [],
        "text": "Item Three"
      }
    ],
    "level": 1,
    "listItem": "number",
    "markDefs": [],
    "style": "normal"
  }
],

BlockContent Component using PortableText

// react | gatsby
import React from 'react';
// third-party
import { PortableText, PortableTextProps } from '@portabletext/react';
// styles
import styles from './BlockContent.module.scss';

export const BlockContent = ({ value }: PortableTextProps) => {
  return (
    <div className={styles.block_content}>
      <PortableText
        value={value}
        components={{
          marks: {
            link: ({ value, children }) => {
              const target = (value?.href || '').startsWith('http') ? '_blank' : undefined;
              return (
                <a href={value?.href} target={target} rel={target === '_blank' && 'noreferrer noopener'}>
                  {children}
                </a>
              );
            },
          },
          list: {
            bullet: ({ children }) => <ul style={{ listStyleType: 'disc' }}>{children}</ul>,
            number: ({ children }) => <ol style={{ listStyleType: 'number' }}>{children}</ol>,
          },
        }}
      />
    </div>
  );
};

What is rendered

<div>
  <h2>Block Content</h2>
  <p></p>
  <p>
    "Lorem ipsum dolor sit amet, <span class='unknown__pt__mark__fb9b93ad297e'>consectetur</span> adipiscing elit,
    sed do eiusmod tempor incididunt ut labore et dolore "magna" aliqua. Ut enim ad minim veniam, quis nostrud
    exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit
    in voluptate velit esse cillum dolore eu fugiat nulla pariatur.{' '}
    <span class='unknown__pt__mark__917ed6187abc'>Excepteur sint occaecat</span> cupidatat non proident, sunt in
    culpa qui officia deserunt mollit anim id est laborum."
  </p>
  <p></p>
  <p>Item One</p>
  <p>Item Two</p>
  <p>Item Three</p>
  <p></p>
  <p>Item One</p>
  <p>Item Two</p>
  <p>Item Three</p>
</div>

Environment

  • "@portabletext/react": "^1.0.2"
  • "gatsby": "^4.7.1",
  • "gatsby-source-sanity": "^7.3.2",
  • "@sanity/all relative packages": "^2.27.0"

Build Fail due to ReferenceError: createElement is not defined

I can’t build my application anymore on the pages that use Portable Text. In Vercel, I get this build error:

ReferenceError: createElement is not defined
--
12:09:13.246 | at renderBlock (/vercel/path0/web/node_modules/@portabletext/react/dist/react-portable-text.js:397:5)
12:09:13.247 | at renderNode (/vercel/path0/web/node_modules/@portabletext/react/dist/react-portable-text.js:274:14)
12:09:13.247 | at /vercel/path0/web/node_modules/@portabletext/react/dist/react-portable-text.js:245:48
12:09:13.247 | at Array.map (<anonymous>)
12:09:13.247 | at PortableText (/vercel/path0/web/node_modules/@portabletext/react/dist/react-portable-text.js:245:27)
12:09:13.247 | at Wc (/vercel/path0/web/node_modules/react-dom/cjs/react-dom-server.browser.production.min.js:68:44)
12:09:13.247 | at Zc (/vercel/path0/web/node_modules/react-dom/cjs/react-dom-server.browser.production.min.js:70:253)
12:09:13.247 | at Z (/vercel/path0/web/node_modules/react-dom/cjs/react-dom-server.browser.production.min.js:76:89)
12:09:13.248 | at $c (/vercel/path0/web/node_modules/react-dom/cjs/react-dom-server.browser.production.min.js:78:98)
12:09:13.248 | at bd (/vercel/path0/web/node_modules/react-dom/cjs/react-dom-server.browser.production.min.js:77:404)

Running Next.js 13.5.4, happens locally and when deploying.

Custom objects with children portable text objects are rendered as plain blocks

The problem

Custom objects that contain portable text blocks, spans or other typed objects in the children property are rendered as plain blocks. The custom serializer provided in the types is not used in this case, and a regular renderer with a p tag with a subsequent traversal is used.

For example, an object like this will be affected:

interface CustomObject {
  _type: 'customObject';
  children: PortableTextBlock[];
  someCustomProperty: any;
}

You can see a reproduction in this CodeSandbox (the demo was updated): https://codesandbox.io/p/github/dbanisimov/react-portabletext-repro/draft/jovial-jerry.

Expected behavior

  • The custom block should be rendered using the provided serializer according to the _type property
  • (optional) The traversal might continue with the children property of the custom object. This is how it used to work in sanity-io/block-content-to-react library, but it might not work for someone who expects to do some custom rendering of children objects.

Possible culprit

The check on this line is using a loose assertion of the block type from @portabletext/toolkit that accepts any block-like object without checking the type. This check happens before the custom node type check, so it always takes precedence.

Possible fixes

  1. Stricter type checking in this library, e.g.:
    if (isPortableTextBlock(node) && node._type === 'block') {
      return renderBlock(node, index, key, isInline)
    }
  1. Stricter type checking in isPortableTextBlock from @portabletext/toolkit. This will make that check much clearer for the user, but the change might break something that depends on it.
  2. Handle custom object types when rendering blocks. Function renderBlock can be changed to check the _type property on a node, call a custom serializer if one found and use the default children traversal.

Use case

Just to provide the context for using custom objects with children that are typed objects themselves:
We're implementing a custom 'link' block. As with normal links it can contain other formatted text spans within it. The natural solution for that was to use portable text within the link itself. We are using a node here instead of a mark, because it's easier to work with nodes than to convert markDefs back and forth. It makes it easier to integrate with other rich text editing libraries that handle links in the nodes tree.

Getting TypeScript errors when using block component

Given this basic setup from the README:

const components = {
  block: {
    h1: ({children}) => <h1 className="text-2xl">{children}</h1>,
  }
};

<PortableText value={body} components={components} />

The TypeScript Engine will report this problem on the <PortableText> components property:

Type '{ block: { h1: ({ children }: { children: any; }) => Element; }; }' is not assignable to type 'Partial<PortableTextReactComponents>'.
  Types of property 'block' are incompatible.
    Type '{ h1: ({ children }: { children: any; }) => Element; }' is not assignable to type 'PortableTextBlockComponent | Record<string, PortableTextBlockComponent>'.
      Type '{ h1: ({ children }: { children: any; }) => Element; }' is not assignable to type 'Record<string, PortableTextBlockComponent>'.
        Property 'h1' is incompatible with index signature.
          Type '({ children }: { children: any; }) => Element' is not assignable to type 'PortableTextBlockComponent'.
            Type '({ children }: { children: any; }) => Element' is not assignable to type 'FunctionComponent<PortableTextComponentProps<PortableTextBlock<PortableTextMarkDefinition, ArbitraryTypedObject | PortableTextSpan, string, string>>>'.
              Types of parameters '__0' and 'props' are incompatible.
                Type 'PropsWithChildren<PortableTextComponentProps<PortableTextBlock<PortableTextMarkDefinition, ArbitraryTypedObject | PortableTextSpan, string, string>>>' is not assignable to type '{ children: any; }'.
                  Property 'children' is optional in type 'PortableTextComponentProps<PortableTextBlock<PortableTextMarkDefinition, ArbitraryTypedObject | PortableTextSpan, string, string>> & { ...; }' but required in type '{ children: any; }'.ts(2322)
[types.ts(32, 3): ]()The expected type comes from property 'components' which is declared here on type 'IntrinsicAttributes & PortableTextProps<any>'

The front-end renders fine, but I'm trying to deploy this using NextJS / Vercel and the build is failing.

Note that the problem isn't occurring when using marks, or types.

Can't do custom components

Following the docs here: https://github.com/portabletext/react-portabletext#types

I have a field with _type="testimonial", and when I have the following object for components, PortableText doesn't do anything.

const myPortableTextComponents = {
      block: {
        h1: headingHandler,
        h2: headingHandler,
        h3: headingHandler,
        h4: headingHandler,
        h5: headingHandler,
        h6: headingHandler,
      },
      types: {
        testimonial: (props) => {
          console.log(
            '\n\n\n\n**************\n** hey **\n**************\n\n\n\n'
          )

          console.log(props)
          return null
        },
      },
      testimonial: (props) => {
        console.log(
          '\n\n\n\n**************\n** hey **\n**************\n\n\n\n'
        )

        console.log(props)
        return null
      },
}

<PortableText
          value={node._rawContent}
          components={myPortableTextComponents}
        />

As you can see, I'm trying the types object and the top-level for the components object. Neither work.

cannot override types block

it seems that the lib does not handle block customisation in types. I did like this

types: { block: ({ children }) => { console.log("a"); return <span>{children}</span>; }, },

and it does not do anything. I need to do in block part instead

Module Resolution issues when building with Astro

I have an issue with using this package to build a static site with Astro.

I've created a Codesandbox that renders Portable text.

Codesandbox

The command npm run dev sets up a development server and everything resolves and renders fine.

npm run build throws an error when trying to import the module.

generating static routes 
 error   Named export 'PortableText' not found. The requested module '@portabletext/react' is a CommonJS module, which may not support all module.exports as named exports.
  CommonJS modules can always be imported via the default export, for example using:
  
  import pkg from '@portabletext/react';
  const { PortableText } = pkg;
  
    at ModuleJob._instantiate (node:internal/modules/esm/module_job:123:21)
    at async ModuleJob.run (node:internal/modules/esm/module_job:189:5)
    at async Promise.all (index 0)
    at async ESMLoader.import (node:internal/modules/esm/loader:533:24)
    at async generatePages (file:///project/home/jumbolove/workspace/node_modules/astro/dist/core/build/generate.js:70:20)
    at async staticBuild (file:///project/home/jumbolove/workspace/node_modules/astro/dist/core/build/static-build.js:68:7)
    at async AstroBuilder.build (file:///project/home/jumbolove/workspace/node_modules/astro/dist/core/build/index.js:83:5)
    at async AstroBuilder.run (file:///project/home/jumbolove/workspace/node_modules/astro/dist/core/build/index.js:123:7)
    at async build (file:///project/home/jumbolove/workspace/node_modules/astro/dist/core/build/index.js:22:3)
    at async runCommand (file:///project/home/jumbolove/workspace/node_modules/astro/dist/cli/index.js:138:14)

I tried the suggestion of importing the whole package, then destructuring the Portable text function, and that also failed to resolve with a different error.

Since the dev server works fine, I'm wondering if there is a config setup missing that treats modules differently on dev vs. build.

Let me know if this is a bug in the way the module is bundled or if the Astro build config should resolve the module with different configs.

Dependency Dashboard

This issue lists Renovate updates and detected dependencies. Read the Dependency Dashboard docs to learn more.


Using a curated preset maintained by


Sanity: The Composable Content Cloud

Warning

These dependencies are deprecated:

Datasource Name Replacement PR?
npm @babel/plugin-proposal-object-rest-spread Unavailable

Pending Approval

These branches will be created by Renovate only once you click their checkbox below.

  • chore(deps): update non-major (@sanity/pkg-utils, @sanity/ui, @typescript-eslint/eslint-plugin, @typescript-eslint/parser, @vitejs/plugin-react, esbuild, prettier, vite)
  • chore(deps): update pnpm to v9.3.0
  • chore(deps): update dependency npm-run-all2 to v6
  • chore(deps): update dependency react-refractor to v3
  • chore(deps): update peaceiris/actions-gh-pages action to v4
  • chore(deps): lock file maintenance
  • 🔐 Create all pending approval PRs at once 🔐

Ignored or Blocked

These are blocked by an existing closed PR and will not be recreated unless you click a checkbox below.

Detected dependencies

github-actions
.github/workflows/format-if-needed.yml
  • actions/checkout v4
  • actions/setup-node v4
  • actions/create-github-app-token v1
  • peter-evans/create-pull-request v6@6d6857d36972b65feb161a90e484f2984215f83e
.github/workflows/main.yml
  • actions/checkout v4
  • actions/setup-node v4
  • actions/checkout v4
  • actions/setup-node v4
.github/workflows/release-please.yml
  • actions/create-github-app-token v1
  • google-github-actions/release-please-action v3
  • actions/checkout v4
  • pnpm/action-setup v2
  • actions/setup-node v4
  • peaceiris/actions-gh-pages v3@373f7f263a76c20808c831209c920827a82a2847
npm
package.json
  • @portabletext/toolkit ^2.0.15
  • @portabletext/types ^2.0.13
  • @babel/plugin-proposal-object-rest-spread ^7.20.7
  • @commitlint/cli ^19.3.0
  • @commitlint/config-conventional ^19.2.2
  • @sanity/pkg-utils ^6.9.1
  • @sanity/ui ^2.3.1
  • @types/leaflet ^1.9.12
  • @types/react ^18.3.3
  • @types/react-dom ^18.3.0
  • @types/refractor ^3.4.1
  • @types/ws ^8.5.10
  • @typescript-eslint/eslint-plugin ^7.12.0
  • @typescript-eslint/parser ^7.12.0
  • @vitejs/plugin-react ^4.3.0
  • commitizen ^4.3.0
  • cz-conventional-changelog ^3.3.0
  • esbuild ^0.21.4
  • esbuild-register ^3.5.0
  • eslint ^8.57.0
  • eslint-config-prettier ^9.1.0
  • eslint-config-sanity ^7.1.2
  • eslint-plugin-react ^7.34.2
  • eslint-plugin-react-compiler 0.0.0-experimental-c8b3f72-20240517
  • eslint-plugin-react-hooks ^4.6.2
  • leaflet ^1.9.4
  • npm-run-all2 ^5.0.2
  • prettier ^3.3.1
  • prettier-plugin-packagejson ^2.5.0
  • react ^18.3.1
  • react-dom ^18.3.1
  • react-is ^18.3.1
  • react-leaflet ^4.2.1
  • react-refractor ^2.2.0
  • refractor ^4.8.1
  • rimraf ^5.0.1
  • rollup-plugin-visualizer ^5.12.0
  • styled-components ^6.1.11
  • typescript ^5.4.5
  • vite ^5.2.13
  • vitest ^1.6.0
  • react ^17 || ^18 || >=19.0.0-rc
  • node ^14.13.1 || >=16.0.0
  • pnpm 9.1.3

  • Check this box to trigger a request for Renovate to run again on this repository

Unknown mark type, specify a component for it false positive?

So I use import { PortableText } from '@portabletext/react' in my Next.js application which uses Sanity as a CMS. Inside Sanity I have the following in my block content

marks: {
        // Decorators usually describe a single property – e.g. a typographic
        // preference or highlighting by editors.
        decorators: [
          { title: "Strong", value: "strong" },
          { title: "Emphasis", value: "em" },
        ],
        // Annotations can be any object structure – e.g. a link or a footnote.
        annotations: [
          {
            name: "internalLink",
            title: "Internal Link",
            type: "object",
            icon: PlugIcon,
            fields: [
              {
                title: "Reference",
                name: "reference",
                type: "reference",
                to: [{ type: "post" }],
              },
            ],
          },
        ],
      },

Inside my Next.js application I have the following

import Link from 'next/link'
import { PortableText } from '@portabletext/react'

const InternalLink = ({ children, value }) => (
  <Link href={`/${value.slug.current}`} prefetch={false}>
    <a>{children}</a>
  </Link>
)
const myPortableTextComponents = {
  types: {
    image: ImageComponent,
  },
  marks: {
    internalLink: InternalLink,
  },
}

export const PortableTextComponent = ({ value }) => (
  <PortableText components={myPortableTextComponents} value={value}  />
)

However, when I build my application I get the following error

Unknown mark type "internalLink", specify a component for it in the `components.marks` option

I am not sure where else defining internalLink needs to happen in order for this to go away... Adding onMissingComponent={false} also does not suppress it either.

Unknown block type "undefined", specify a component for it in the `components.types` prop

Im getting error Unknown block type "undefined", specify a component for it in the `components.types` prop when im trying to display data from sanity in <PortableText/>

Data from sanity:

{
  "ms": 16,
  "query": "*[_type==\"eucharistDates\"]{dates}",
  "result": [
    {
      "dates": [
        {
          "_key": "05aa4a7799a3",
          "_type": "block",
          "children": [
            {
              "_key": "2173ec501f7b",
              "_type": "span",
              "marks": [
                
              ],
              "text": "Sobota 9:00 10:00"
            }
          ],
          "markDefs": [
            
          ],
          "style": "normal"
        },
        {
          "_key": "ddf850d31eb2",
          "_type": "block",
          "children": [
            {
              "_key": "0fa21a90dcbc",
              "_type": "span",
              "marks": [
                
              ],
              "text": "Niedziela 8:00 10:00 13:00"
            }
          ],
          "markDefs": [
            
          ],
          "style": "normal"
        }
      ]
    }
  ]
}

Next.js code:

import Image from 'next/image';
import PropTypes from 'prop-types';
import { PortableText } from '@portabletext/react';
import heroImage from '../../../../public/hero.jpg';

const components = {
  block: {
    normal: ({ children }) => <span>{children}</span>,
  },
};

const Hero = ({ eucharistDates }) => {
  return (
    <div className="h-[calc(100vh_-_5rem)] w-screen relative bg-black">
      <section className="absolute left-1/2 top-1/2 z-10 flex flex-col text-white text-center transform -translate-x-1/2 -translate-y-1/2">
        <h1 className="uppercase text-4xl">
          Parafia św. Bartłomieja w Opocznie
        </h1>
        <PortableText value={eucharistDates} components={components} />
      </section>
      <Image
        src={heroImage}
        layout="fill"
        objectFit="cover"
        placeholder="blur"
        draggable={false}
      />
    </div>
  );
};

Does not work with lists

Same as in an already closed but not solved issue in block-content-to-react #issue48

List items do not work with react-portable text. For reference I am using Next Js.


import { urlFor } from 'libs/sanity';
import { PortableText } from '@portabletext/react';

const ptComponents = {
  types: {
    list: {
      bullet: ({ children }) => (
        <ul className="list-disc list-inside p-4 bg-accent-7">{children}</ul>
      ),
    },
    listItem: {
      // Ex. 1: customizing common list types
      bullet: ({ children }) => (
        <li className="text-sm text-indigo">XX{children}</li>
      ),
    },
    image: ({ value }) => {
      if (!value?.asset?._ref) {
        return null;
      }
      return (
        <Image
          alt={value.alt || ' '}
          src={urlFor(value).width(320).height(240).fit('max').auto('format')}
          layout="fill"
          objectFit="contain"
        />
      );
    },
  },
};


      <div className="text-sm list-disc list-inside p-4 bg-accent-7">
        <PortableText
          value={product.productSummary}
          components={ptComponents}
        />
      </div>



Can't get item position a list

I'm trying to create functionality in my code where nested Items in a list may have different bullets if they're nested. Accessing the level inside value is enough for unordered items but for ordered items, I'd need the item element position with regard to the parent as well. i.e I want to render something like the sanity editor shows while nesting a list:
Screen Shot 2023-02-24 at 6 31 33 PM
In this case the type attribute in <ol/> works fine but for any other custom ordered kind of bullet I'd need the position in the list.

looking at the types index seems like a good candidate, but when I try to get the index I only get repeated 1's and 3's which either seem broken or its purpose is different, my workaround was to mutate the value prop in the list to add an order property like this:

bullet: ({ children }) => {
        return (
            <ul className={style?.bullet}>
                {Children.map(children, (child, idx) => {
                    if (isValidElement(child)) {
                        child.props.value.order = idx;
                    }
                    return child;
                })}
            </ul>
        );
    },

And then in the listItem I've access to level and order to render the bullet that I want, but I wanted to ask if is there a better way, or yes it is, index is the answer but is not actually working.

react-portable-text has no property 'toPlainText"

when building my site Im running into this error both locally and during deployments to vercel.

--
10:34:43.698 |  
10:34:43.699 | 1 \| import cjs from './react-portable-text.js';
10:34:43.699 | 2 \|
10:34:43.699 | > 3 \| export const toPlainText = cjs.toPlainText;
10:34:43.699 | \|                            ^
10:34:43.699 | 4 \| export const PortableText = cjs.PortableText;
10:34:43.699 | 5 \| export const defaultComponents = cjs.defaultComponents;
10:34:43.699 | 6 \| export const mergeComponents = cjs.mergeComponents;
10:34:43.699 |  
10:34:43.699 |  
10:34:43.699 | WebpackError: TypeError: Cannot read properties of undefined (reading 'toPlain  Text')
10:34:43.699 |  
10:34:43.699 | - react-portable-text.cjs.mjs:3
10:34:43.699 | [retool-dot-com]/[@portabletext]/react/dist/react-portable-text.cjs.mjs:3:28
10:34:43.700 | - bootstrap:19
10:34:43.700 | retool-dot-com/webpack/bootstrap:19:1
10:34:43.700 |  
10:34:43.700 | - pageSanity.js:9
10:34:43.700 | retool-dot-com/src/templates/pageSanity.js:9:9

CleanShot 2023-07-21 at 11 05 23

It makes it seem like this would be the dep packages issue but when using react-portable-text directly there is no issue. it builds and runs perfectly. there is something about the implementation or version of this package in your package that causes these errors.

Gatsby v4.x
node v16.x
babel/core v7.6

tested this on your package from version 2.0.3 -> 3.0.4 all resulted in the same error.
simply installing react-portable-text directly and only replacing the props of that component to be serializers and content this builds and runs with no issues.

Does not currently work with React Native.

Does not currently work with React Native. So no migration path of old implementation.

const data = [ { _key: "9ec013feda12", _type: "block", children: [ { _key: "09308a7bb41d0", _type: "span", marks: [], text: "Some text here...", }, ], markDefs: [], style: "normal", }, ];

OLD Implementation works fine
<BlockContent blocks={data} />

New
<PortableText value={data} />

Gives error
`Error: Text strings must be rendered within a component.

This error is located at:
in strong (created by strong)
in strong (created by ae)
in p (created by normal)
in normal (created by ae)
in ae (created by ArticleDetailsScreen)
in RCTView (created by View)
in View (created by ArticleDetailsScreen)
in RCTScrollContentView (created by ScrollView)
in RCTScrollView (created by ScrollView)
in ScrollView (created by ScrollView)
in ScrollView (created by ArticleDetailsScreen)
in RCTSafeAreaView
in SafeAreaView (created by ArticleDetailsScreen)
in ArticleDetailsScreen (created by SceneView)
in StaticContainer
in EnsureSingleNavigator (created by SceneView)
in SceneView (created by SceneView)
in RCTView (created by View)
in View (created by DebugContainer)
in DebugContainer (created by MaybeNestedStack)
in MaybeNestedStack (created by SceneView)
in RNSScreen (created by AnimatedComponent)
in AnimatedComponent
in AnimatedComponentWrapper (created by Screen)
in MaybeFreeze (created by Screen)
in Screen (created by SceneView)
in SceneView (created by NativeStackViewInner)
in RNSScreenStack (created by ScreenStack)
in ScreenStack (created by NativeStackViewInner)
in NativeStackViewInner (created by NativeStackView)
in RCTView (created by View)
in View (created by SafeAreaInsetsContext)
in SafeAreaProviderCompat (created by NativeStackView)
in NativeStackView (created by NativeStackNavigator)
in Unknown (created by NativeStackNavigator)
in NativeStackNavigator (created by HomeStackNavigator)
in HomeStackNavigator (created by SceneView)
in StaticContainer
in EnsureSingleNavigator (created by SceneView)
in SceneView (created by BottomTabView)
in RCTView (created by View)
in View (created by Screen)
in RCTView (created by View)
in View (created by Background)
in Background (created by Screen)
in Screen (created by BottomTabView)
in RNSScreen (created by AnimatedComponent)
in AnimatedComponent
in AnimatedComponentWrapper (created by Screen)
in MaybeFreeze (created by Screen)
in Screen (created by MaybeScreen)
in MaybeScreen (created by BottomTabView)
in RNSScreenNavigationContainer (created by ScreenContainer)
in ScreenContainer (created by MaybeScreenContainer)
in MaybeScreenContainer (created by BottomTabView)
in RNCSafeAreaProvider (created by SafeAreaProvider)
in SafeAreaProvider (created by SafeAreaInsetsContext)
in SafeAreaProviderCompat (created by BottomTabView)
in BottomTabView (created by BottomTabNavigator)
in Unknown (created by BottomTabNavigator)
in BottomTabNavigator (created by TabNavigator)
in TabNavigator (created by RootNavigator)
in EnsureSingleNavigator
in BaseNavigationContainer
in ThemeProvider
in NavigationContainerInner (created by RootNavigator)
in RootNavigator (created by App)
in QueryClientProvider (created by ReactQueryProvider)
in ReactQueryProvider (created by App)
in AuthProvider (created by App)
in App (created by ExpoRoot)
in ExpoRoot
in RCTView (created by View)
in View (created by AppContainer)
in DevAppContainer (created by AppContainer)
in RCTView (created by View)
in View (created by AppContainer)
in AppContainer
at node_modules/react-native/Libraries/Core/ExceptionsManager.js:104:6 in reportException
at node_modules/react-native/Libraries/Core/ExceptionsManager.js:172:19 in handleException
at node_modules/react-native/Libraries/Core/setUpErrorHandling.js:24:6 in handleError
at node_modules/expo-error-recovery/build/ErrorRecovery.fx.js:12:21 in ErrorUtils.setGlobalHandler$argument_0`

Tested with
"expo": "~44.0.0"
"react": "17.0.1",
"react-native": "0.64.3",
@portabletext/react": "^1.0.6"

Type error on build  – 'Li' cannot be used as a JSX component.

I started to get this type error during build step on Vercel 24 hours ago. Latest sucessfully build was 3 days ago:
Type error: 'Li' cannot be used as a JSX component. in @portabletext/react

Project: Next JS, Sanity.

Dependencies I use (the relevant parts):

...
"@portabletext/react": "^1.0.4",
"next": "12.1.0",
"react": "17.0.2",
"react-dom": "17.0.2",
"@portabletext/types": "^1.0.3",
"@types/react": "^17.0.42",
"@typescript-eslint/eslint-plugin": "^5.16.0",
"@typescript-eslint/parser": "^5.16.0",
"eslint": "8.11.0",
"eslint-config-next": "12.1.0",
"eslint-config-prettier": "^8.5.0",
"eslint-import-resolver-typescript": "^2.7.0",
"typescript": "^4.6.2"

Error:

./node_modules/@portabletext/react/src/react-portable-text.tsx:136:8
--
09:34:49.278 | Type error: 'Li' cannot be used as a JSX component.
09:34:49.278 | Its element type 'ReactElement<any, any> \| Component<PortableTextComponentProps<PortableTextListItemBlock<PortableTextMarkDefinition, PortableTextSpan, string, string>>, any, any>' is not a valid JSX element.
09:34:49.278 | Type 'Component<PortableTextComponentProps<PortableTextListItemBlock<PortableTextMarkDefinition, PortableTextSpan, string, string>>, any, any>' is not assignable to type 'Element \| ElementClass'.
09:34:49.278 | Type 'Component<PortableTextComponentProps<PortableTextListItemBlock<PortableTextMarkDefinition, PortableTextSpan, string, string>>, any, any>' is not assignable to type 'ElementClass'.
09:34:49.278 | The types returned by 'render()' are incompatible between these types.
09:34:49.278 | Type 'React.ReactNode' is not assignable to type 'import("/vercel/path0/web/node_modules/@types/react-dom/node_modules/@types/react/index").ReactNode'.
09:34:49.278 |  
09:34:49.278 | 134 \|
09:34:49.278 | 135 \|     return (
09:34:49.278 | > 136 \|       <Li key={key} value={node} index={index} isInline={false} renderNode={renderNode}>
09:34:49.278 | \|        ^
09:34:49.279 | 137 \|         {children}
09:34:49.279 | 138 \|       </Li>
09:34:49.279 | 139 \|     )

PortableText Typescript Error

I am upgrading my NextJs/Sanity application to NextJS 13 using Sanity.io and typescript.

The PortableText tag no longer works. The value and components attributes are flagged as an error in Visual Studio Code.

import { PortableText } from "@portabletext/react";

<PortableText value={post?.body} components={components} />

I am getting the following error:

ype 'PortableTextBlockComponent[]' is not assignable to type 'TypedObject | TypedObject[]'. Type 'PortableTextBlockComponent[]' is not assignable to type 'TypedObject[]'. Type 'PortableTextBlockComponent' is not assignable to type 'TypedObject'. Property '_type' is missing in type 'ComponentClass<PortableTextComponentProps<PortableTextBlock<PortableTextMarkDefinition, ArbitraryTypedObject | PortableTextSpan, string, string>>, any>' but required in type 'TypedObject'.ts(2322) index.d.ts(171, 3): '_type' is declared here. react-portable-text.d.ts(163, 3): The expected type comes from property 'value' which is declared here on type 'IntrinsicAttributes & PortableTextProps' (property) PortableTextProps.value: TypedObject | TypedObject[]

Cannot render embedded image

I have a list of headings, body, images, etc.

While rendering the block with the help of react-portabletext in a NEXT JS project with Typescript I don't see the image in the rendered HTML. After I have inspected it to look for the image, i see a div like below:

<div style="display:none">[@portabletext/react] Unknown block type "image", specify a component for it in the components.types prop</div>

In the PortableText component I am using it like this below:

<PortableText value={data.body} onMissingComponent={false} />

Usage with Preact getting error: Type 'Element' is not assignable to type 'ReactNode'.

Hi! 👋

Firstly, thanks for your work on this project! 🙂

Today I used patch-package to patch @portabletext/[email protected] for the project I'm working on.

Here is the diff that solved my problem:

diff --git a/node_modules/@portabletext/react/src/react-portable-text.tsx b/node_modules/@portabletext/react/src/react-portable-text.tsx
index 0b68a71..fb38751 100644
--- a/node_modules/@portabletext/react/src/react-portable-text.tsx
+++ b/node_modules/@portabletext/react/src/react-portable-text.tsx
@@ -79,7 +79,7 @@ const getNodeRenderer = (
   components: PortableTextReactComponents,
   handleMissingComponent: MissingComponentHandler
 ): NodeRenderer => {
-  function renderNode<N extends TypedObject>(options: Serializable<N>): ReactNode {
+  function renderNode<N extends TypedObject>(options: Serializable<N>): JSX.Element | string {
     const {node, index, isInline} = options
     const key = node._key || `node-${index}`
 

This issue body was partially generated by patch-package.

  • Using Preact
  • Using Portable Text
  • This error happened
✖ ERROR ./node_modules/@portabletext/react/src/react-portable-text.tsx
ERROR in ./node_modules/@portabletext/react/src/react-portable-text.tsx(87,7):
TS2322: Type 'Element' is not assignable to type 'ReactNode'.
error Command failed with exit code 1.

Managed to get builds working by changing it to JSX.Element | string

Here's my TS config

{
  "compilerOptions": {
    "strict": true,
    "esModuleInterop": true,
    "lib": ["DOM", "ES2022"],
    "jsx": "react-jsx",
    "jsxImportSource": "preact",
    /* allowJs
     *
     * Allows us to use Javascript and Typescript files
     * together. This is because we built the new calculators using Typescript.
     */
    "allowJs": true,
    "noEmit": true /* Do not emit outputs. */,
    "skipLibCheck": true,
    "plugins": [
      {
        "name": "typescript-plugin-css-modules",
        "options": {
          /* Regex that will match anything like `*.css` when used in a ts file. */
          "customMatcher": ".+\\.css"
        }
      }
    ]
  },
  "include": ["src/**/*"]
}

cc @SamScott3000

[Feature request] Add a component wrapper prop

I would love to wrap all of my type serializers in error boundaries. Instead of adding one to each of my serializers, I would love to have a prop that I could provide a component that wraps all serializers.

Example usage:

<PortableText 
  value={value} 
  components={components} 
  componentWrapper={({ children }) => (
    <ErrorBoundary fallback={<div>Error</div>}>
      {children}
    </ErrorBoundary>
  )}
/>

Is this something you could look into?

unknownBlockStyle is not working / does nothing

The component override unknownBlockStyle is not working. It seems that when you provide a block component override, it is merged with the default block-styles, so that there never is an "unknown" block.
For instance if you define

  block: {
    // Ex. 1: customizing common block types
    h1: ({children}) => <h1 className="text-2xl">{children}</h1>,
    blockquote: ({children}) => <blockquote className="border-l-purple-500">{children}</blockquote>,
  },
  unknownBlockStyle: ({children}) => <BlockWrapper>{children}</BlockWrapper>,
}

the block style 'normal' should render with BlockWrapper, but instead gets render with the default component (<p>)

'React' is declared but its value is never read.

I am having a hard time using this library as it seems like I'm not picking up the build js files, but rather typescript is using the source files and having opinions about them! My tsconfig.json has skipLibCheck: true. It seems that it is not finding the correct export? I get the above error just by running tsc --noEmit

Right now my solution is to patch comment out the React references so my project will build.

I pulled down the repo to check things out. It looks like the tsconfig could be updated with "jsx": "react-jsx" as with React 17+, you no longer need to ghost import React to handle jsx and they suggest moving away from it. After updating the react eslint extends with "plugin:react/jsx-runtime" and removing the react import, all seemed well...

Except I can't build because of linting errors in react-portable-text.tsx that exist on main...

Are there settings I need to adjust on my end or can I help put up PR once the build issues get resolved?

Change to .mjs breaks usage in Expo web (React Native)

Just updated @portabletext/react to v1.0.4, unfortunately the package no longer functions out of the box under Expo web.

Looks to be related to the change to using .mjs. Can confirm it works when downgrading back to v1.0.3.

Might be related to this: facebook/metro#535, however the package seems to work fine under other (native) Expo targets (Android/iOS).

Minimum repo here: https://github.com/4lun/expo-portabletext-react (run yarn web)

Error from expo-cli:

###/node_modules/@portabletext/react/dist/react-portable-text.mjs 162:54-73
Can't import the named export 'LIST_NEST_MODE_HTML' from non EcmaScript module (only default export is available)
###/node_modules/@portabletext/react/dist/react-portable-text.mjs 307:15-29
Can't import the named export 'buildMarksTree' from non EcmaScript module (only default export is available)
###/node_modules/@portabletext/react/dist/react-portable-text.mjs 144:38-51
Can't import the named export 'createContext' from non EcmaScript module (only default export is available)
###/node_modules/@portabletext/react/dist/react-portable-text.mjs 186:8-27
Can't import the named export 'isPortableTextBlock' from non EcmaScript module (only default export is available)
###/node_modules/@portabletext/react/dist/react-portable-text.mjs 180:8-35
Can't import the named export 'isPortableTextListItemBlock' from non EcmaScript module (only default export is available)
###/node_modules/@portabletext/react/dist/react-portable-text.mjs 177:8-33
Can't import the named export 'isPortableTextToolkitList' from non EcmaScript module (only default export is available)
###/node_modules/@portabletext/react/dist/react-portable-text.mjs 183:8-33
Can't import the named export 'isPortableTextToolkitSpan' from non EcmaScript module (only default export is available)
###/node_modules/@portabletext/react/dist/react-portable-text.mjs 189:8-37
Can't import the named export 'isPortableTextToolkitTextNode' from non EcmaScript module (only default export is available)
###/node_modules/@portabletext/react/dist/react-portable-text.mjs 162:17-26
Can't import the named export 'nestLists' from non EcmaScript module (only default export is available)
###/node_modules/@portabletext/react/dist/react-portable-text.mjs 250:12-27
Can't import the named export 'spanToPlainText' from non EcmaScript module (only default export is available)
###/node_modules/@portabletext/react/dist/react-portable-text.mjs 163:27-37
Can't import the named export 'useContext' from non EcmaScript module (only default export is available)
###/node_modules/@portabletext/react/dist/react-portable-text.mjs 149:16-23
Can't import the named export 'useMemo' from non EcmaScript module (only default export is available)
###/node_modules/@portabletext/react/dist/react-portable-text.mjs 164:21-28
Can't import the named export 'useMemo' from non EcmaScript module (only default export is available)
###/node_modules/@portabletext/react/dist/react-portable-text.mjs 167:21-28
Can't import the named export 'useMemo' from non EcmaScript module (only default export is available)
###/node_modules/@portabletext/react/dist/react-portable-text.mjs 33:0-52
Can't reexport the named export 'toPlainText' from non EcmaScript module (only default export is available)

Error in browser console:

Uncaught TypeError: undefined is not a function
    at Module.../../node_modules/@portabletext/react/dist/react-portable-text.mjs (static/js/bundle.js:6036:83)

Not clear to me if this is mostly an issue with Expo, and/or if there's potentially some sort of webpack config change I can make to resolve it. Any pointers greatly appreciated.

typescript example blog rendering of text, images and youtube videos

A real example from a blog with next js, typescript and sanity of how to render text, images and youtube videos would be very helpful. If anyone has an example, it would be greatly appreciated, since I find the @portabletext/react guides a bit confusing and they generate errors. Thank you very much.

Typescript errors

I'm building a project with Sanity, Typescript, ViteJS and React, and wanted to incorporate the PortableText component.

Unfortunately, when running tsc I get errors from this package, or more specifically from the toolkit package. See the errors below.
Seems like this is due to my tsconfig including this setting: "noUncheckedIndexedAccess": true,

I would expect to be able to use this package with that setting enabled. I think the fix must be employed in the toolkit package. Could you enable the tsconfig setting noUncheckedIndexedAccess in toolkit and then in this repo?

node_modules/@portabletext/react/src/toolkit/buildMarksTree.ts:37:7 - error TS2532: Object is possibly 'undefined'.

37       lastNode.children.push({...span, _type: '@span', children: [], markType: '<unknown>'})
         ~~~~~~~~

node_modules/@portabletext/react/src/toolkit/buildMarksTree.ts:46:22 - error TS2532: Object is possibly 'undefined'.

46         const mark = nodeStack[pos].markKey
                        ~~~~~~~~~~~~~~

node_modules/@portabletext/react/src/toolkit/buildMarksTree.ts:67:15 - error TS2532: Object is possibly 'undefined'.

67         _key: span._key,
                 ~~~~

node_modules/@portabletext/react/src/toolkit/buildMarksTree.ts:82:28 - error TS2345: Argument of type 'PortableTextSpan | ArbitraryTypedObject | undefined' is not assignable to parameter of type 'PortableTextSpan | TypedObject'.
  Type 'undefined' is not assignable to type 'PortableTextSpan | TypedObject'.

82     if (isPortableTextSpan(span)) {
                              ~~~~

node_modules/@portabletext/react/src/toolkit/buildMarksTree.ts:91:58 - error TS2769: No overload matches this call.
  Overload 1 of 2, '(...items: ConcatArray<ToolkitTextNode | ToolkitNestedPortableTextSpan<MarkDefinition> | ArbitraryTypedObject>[]): (ToolkitTextNode | ... 1 more ... | ArbitraryTypedObject)[]', gave the following error.
    Argument of type 'ArbitraryTypedObject | undefined' is not assignable to parameter of type 'ConcatArray<ToolkitTextNode | ToolkitNestedPortableTextSpan<MarkDefinition> | ArbitraryTypedObject>'.
      Type 'undefined' is not assignable to type 'ConcatArray<ToolkitTextNode | ToolkitNestedPortableTextSpan<MarkDefinition> | ArbitraryTypedObject>'.
  Overload 2 of 2, '(...items: (ToolkitTextNode | ToolkitNestedPortableTextSpan<MarkDefinition> | ArbitraryTypedObject | ConcatArray<...>)[]): (ToolkitTextNode | ... 1 more ... | ArbitraryTypedObject)[]', gave the following error.
    Argument of type 'ArbitraryTypedObject | undefined' is not assignable to parameter of type 'ToolkitTextNode | ToolkitNestedPortableTextSpan<MarkDefinition> | ArbitraryTypedObject | ConcatArray<...>'.
      Type 'undefined' is not assignable to type 'ToolkitTextNode | ToolkitNestedPortableTextSpan<MarkDefinition> | ArbitraryTypedObject | ConcatArray<...>'.

91       currentNode.children = currentNode.children.concat(span)
                                                            ~~~~


node_modules/@portabletext/react/src/toolkit/buildMarksTree.ts:122:43 - error TS2532: Object is possibly 'undefined'.

122     occurences[mark] = occurences[mark] ? occurences[mark] + 1 : 1
                                              ~~~~~~~~~~~~~~~~

node_modules/@portabletext/react/src/toolkit/buildMarksTree.ts:128:28 - error TS2345: Argument of type 'PortableTextSpan | TypedObject | undefined' is not assignable to parameter of type 'PortableTextSpan | TypedObject'.
  Type 'undefined' is not assignable to type 'PortableTextSpan | TypedObject'.

128         isPortableTextSpan(sibling) &&
                               ~~~~~~~

node_modules/@portabletext/react/src/toolkit/buildMarksTree.ts:171:9 - error TS2532: Object is possibly 'undefined'.

171     if (node._type === '@span' && node.children) {
            ~~~~

node_modules/@portabletext/react/src/toolkit/buildMarksTree.ts:171:35 - error TS2532: Object is possibly 'undefined'.

171     if (node._type === '@span' && node.children) {
                                      ~~~~

node_modules/@portabletext/react/src/toolkit/nestLists.ts:23:26 - error TS2345: Argument of type 'T | undefined' is not assignable to parameter of type 'TypedObject | PortableTextBlock<MarkDefinition, PortableTextSpan | ArbitraryTypedObject>'.
  Type 'undefined' is not assignable to type 'TypedObject | PortableTextBlock<MarkDefinition, PortableTextSpan | ArbitraryTypedObject>'.

23     if (!isListItemBlock(block)) {
                            ~~~~~

node_modules/@portabletext/react/src/toolkit/nestLists.ts:24:17 - error TS2345: Argument of type 'T | undefined' is not assignable to parameter of type 'NestListsOutputNode<T>'.
  Type 'undefined' is not assignable to type 'NestListsOutputNode<T>'.

24       tree.push(block)
                   ~~~~~

node_modules/@portabletext/react/src/toolkit/nestLists.ts:79:38 - error TS2345: Argument of type 'NestListsOutputNode<T> | undefined' is not assignable to parameter of type 'TypedObject | PortableTextBlock<MarkDefinition, PortableTextSpan | ArbitraryTypedObject>'.
  Type 'undefined' is not assignable to type 'TypedObject | PortableTextBlock<MarkDefinition, PortableTextSpan | ArbitraryTypedObject>'.

79       const match = findListMatching(tree[tree.length - 1], block)
                                        ~~~~~~~~~~~~~~~~~~~~~

node_modules/@portabletext/react/src/toolkit/nestLists.ts:94:38 - error TS2345: Argument of type 'NestListsOutputNode<T> | undefined' is not assignable to parameter of type 'TypedObject | PortableTextBlock<MarkDefinition, PortableTextSpan | ArbitraryTypedObject>'.

94       const match = findListMatching(tree[tree.length - 1], {level: block.level || 1})

ReactServerComponentsError when using with Dynamic Routing in Next.js 13.4 App Router configuration

ReactServerComponentsError:

You're importing a component that needs createContext. It only works in a Client Component but none of its parents are marked with "use client", so they're Server Components by default.

import { nestLists, LIST_NEST_MODE_HTML, isPortableTextToolkitList, isPortableTextListItemBlock, isPortableTextToolkitSpan, spanToPlainText, isPortableTextBlock, isPortableTextToolkitTextNode, buildMarksTree } from "@portabletext/toolkit";
export { toPlainText } from "@portabletext/toolkit";
import React, { createContext, useMemo, useContext } from "react";
                     ^^^^^^^^^^^^^
The error was caused by importing '@portabletext/react/dist/react-portable-text.mjs' in './app/blogs/[slug]/page.tsx'.

Maybe one of these should be marked as a client entry with "use client":

The current workaround is likely to wrap it with a customer wrapper that as 'use client'.

typescript example blog rendering of audio player and code block

A real example from a blog with next js, typescript and sanity of how to render audio player and code block would be very helpful. If anyone has an example, it would be greatly appreciated, since I find the @portabletext/react guides a bit difficult to understand. Thank you very much.

Marks Not Rendering

Hi there! Unsure if I'm just doing something wrong, or if it's an actual bug (it's likely the former).

I'm attempting to customize link components coming back from Sanity, and none of the marks are rendering inside the view. Here's a reproduction in CodeSandbox. I modified the text, but left everything else the same as it's coming from Sanity. Am I maybe missing something in the query from Sanity (I'm using the pretty standard blog example schema)?

Here's the body schema for the Sanity schema:

    defineField({
      name: 'body',
      title: 'Body',
      type: 'blockContent'
    })

and the blockContent schema:

    defineArrayMember({
      title: 'Block',
      type: 'block',
      styles: [
        { title: 'Normal', value: 'normal' },
        { title: 'H1', value: 'h1' },
        { title: 'H2', value: 'h2' },
        { title: 'H3', value: 'h3' },
        { title: 'H4', value: 'h4' },
        { title: 'Quote', value: 'blockquote' }
      ],
      lists: [{ title: 'Bullet', value: 'bullet' }],
      marks: {
        decorators: [
          { title: 'Strong', value: 'strong' },
          { title: 'Emphasis', value: 'em' }
        ],
        annotations: [
          {
            title: 'URL',
            name: 'link',
            type: 'object',
            fields: [
              {
                title: 'URL',
                name: 'href',
                type: 'url'
              }
            ]
          }
        ]
      }
    }),

Sanity API Version: 2024-03-10

I should note that in the project where I'm using this, I also have a custom h2 element, and it renders fine. That makes me think I'm missing something obvious with links, or something in the data has changed since the library was last published?

Here's what the HTML output looks like:

Screenshot 2024-03-14 at 12 34 18 PM

And how I'm defining it in Sanity Studio:

Screenshot 2024-03-14 at 12 37 06 PM

Thanks for any help / guidance!

Components documentation

I'm looking for a list of all components that are currently available on portable text that I can serialize, similar with what we find here https://github.com/coreyward/react-portable-text

For instance, under the "block" section in the readme, there's the following comment: // Ex. 1: customizing common block types. Where can I find a list of these common block types?

Warning: validateDOMNesting(...): <p> cannot appear as a descendant of <p>.

I get this console warning when pulling data from Sanity to a React/Gatsby app:

react_devtools_backend.js:3973 Warning: validateDOMNesting(...): <p> cannot appear as a descendant of <p>.
    at p
    at normal (webpack-internal:///./node_modules/@portabletext/react/dist/react-portable-text.mjs:132:14)
    at PortableText (webpack-internal:///./node_modules/@portabletext/react/dist/react-portable-text.mjs:165:10)

Data looks like this (passed as value prop in <Portable Text value={data}/>:

{
  "_key": "422b2df8e2a1",
  "_type": "block",
  "children": [
    {
      "_key": "33d2dcb9b87f0",
      "_type": "span",
      "marks": [],
      "text": "Some text here."
    }
  ],
  "markDefs": [],
  "style": "normal"
}

Happens on all block content. This might not be judged as an error as things do work, but annoying to always have it in the console.

Can't Get Component to work

I am struggling to get this to work. This is my code can anybody give me a steer as to what I am doing wrong? There is no effect from the styling component.

 const myPortableTextComponents = {
        block: {
            h1: ({ children }) => {
                return (
                    <h1 style={{ color: "blue" }}>
                        {children}
                    </h1>
                )
            },
        }
    }

I am calling this with...

                   <PortableText
                        value={page.content}
                        component={myPortableTextComponents}
                    />

Issues using with Nextjs and tailwind

I am using Sanity.io in my Nextjs project. My nextjs app uses Tailwind. The issue is that on my frontend where users can see my site the styling is not matching what is shown in Sanity.io backend. Some examples would be that my bullet list and number list are missing the dots or the numbers as well as the indent. If I go to my global.css and remove @tailwind base; then everything shows correctly on the frontend as it show on Sanity.io backend. It's like Tailwind has conflicts with PortableText.

Again the formatting that is presented on Sanity backend does not show the same while using the default settings Nextjs with Tailwind. However if I remove the following from the default global.css @tailwind base; everything present on Nextjs frontend the way it should The only problem is that I need Tailwind

Children prop no longer available in custom block content

Previously with @sanity/block-content-to-react you could use children in your custom blocks component:

import BaseBlockContent from '@sanity/block-content-to-react';

const components = {
  types: {
    block(props) {
      const { children } = props;
      return <div>{children}</div>;
    }
  },
};

function MyPage(props) {
  return (
    <BaseBlockContent components={components} />
  );
}

Now with this package the attribute is no longer available in props. This is the code I'm using to compute it myself as a workaround, which hopefully helps someone else.

import { PortableText } from '@portabletext/react';
import { buildMarksTree } from '@portabletext/toolkit';

const components = {
  types: {
    block(props) {
      const { value, renderNode } = props;
      const children = buildMarksTree(value).map((child, i) => (
        renderNode({
          node: child,
          isInline: true,
          index: i,
          renderNode,
        })
      ));
      return <div>{children}</div>;
    }
  }
};

function MyPage(props) {
  return (
    <PortableText components={components} />
  );
}

Nested Lists?

I can't find any information on how I can target and style nested lists.
Sanity uses "a series of list item blocks with the same level and listItem properties will be grouped into a virtual one inside of this library." My rich text is inputted as follows

image

and is seen queried like this:

  "2": {
    "children": [{ }],
    "level": 1,
    "listItem": "number",
    "markDefs": [],
    "style": "normal",
    "_key": "2cc0186eb9d3",
    "_type": "block"
  },
  "3": {
    "children": [{ }],
    "level": 2,
    "listItem": "number",
    "markDefs": [],
    "style": "normal",
    "_key": "984f45cfba95",
    "_type": "block"
  },
  "4": {
    "children": [{ }],
    "level": 2,
    "listItem": "number",
    "markDefs": [],
    "style": "normal",
    "_key": "730d9b626b42",
    "_type": "block"
  }
}

But is rendered without grouping the level 2 items with the same indentation treatment?
image


export default function RichText({ variant = 'default', richText }) {

    let components = {
        list: {
            // Ex. 1: customizing common list types
            bullet: ({ children }) => <ul className="mt-xl">{children}</ul>,
            number: ({ children }) => <ol className="mt-lg">{children}</ol>,

            // Ex. 2: rendering custom lists
            checkmarks: ({ children }) => <ol className="m-auto text-lg">{children}</ol>,
        },
        listItem: {
            number: ({ children }) => <Typography className='boo' paragraph={true} component='li' sx={{ color: 'red' }} >{children}</Typography>,
        },
    }

    return (
        <>
            {richText.map((block) => {
                return (
                    <PortableText
                        key={block._key}
                        value={block}
                        components={components}
                    />
                )
            })}
        </>
    )
}; 

How can we target the second level to style them properly? Am I misunderstanding how the grouping of levels should be operating?

How to target p paragraph elements

I'm having trouble targeting the default p tags inside a Block. They are marked as span and as far as I can tell, based on the docs, I should be targeting those correctly. I'm obviously missing something though

The data:

"description": [
              {
                "_key": "0b71cdcbcc5a",
                "_type": "block",
                "children": [
                  {
                    "_key": "259de42897910",
                    "_type": "span",
                    "marks": [],
                    "text": "Some text to see if it works"
                  }
                ],
                "markDefs": [],
                "style": "normal"
              }
            ]

The components (ended up trying whatever I could think of):

const components = {
  block: {
    span: ({ children }) => <Text>{children}</Text>,
    bullet: ({ children }) => <Text>{children}</Text>,
    p: ({ children }) => <Text>{children}</Text>,
  },
  list: {
    bullet: ({ children }) => <View>{children}</View>,
    number: ({ children }) => <Text>{children}</Text>
  },
  listItem: {
    bullet: ({ children }) => <Text>{children}</Text>
  },
  marks: {
    em: ({children}) => <Text>{children}</Text>,
    span: ({children}) => <Text>{children}</Text>,
    p: ({children}) => <Text>{children}</Text>,
  },
};

Any ideas on where I'm going wrong here? It works just fine for Lists n stuff just somehow not the span / p tags

The only kinda alternative I found so far is using toPlainText which works but kinda curious if there is another way

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.