Help with setting up Portable Text in a React app

13 replies
Last updated: Oct 14, 2022
Is anyone willing to hold my hand and walk me through setting up the Portable Text? I have looked for as much information as I can on the matter but I can not seem to figure it out.
I have a basic schema set up:

{
      name: "text",
      title: "Text",
      type: 'array',
      of: [ { type: 'block' } ]
    },
And a mess of a .js file to render the content. (Upgrading from block content to html to portable text - therefore I am just trying to replace what we previously had)

import {toHTML} from '@portabletext/to-html'

const BodyText = ({ text, layoutSettings } = {}) => {
  let className = 'body-text';
  let ls = 'col-6';
  if (layoutSettings?.sizing) {
    ls = layoutSettings.sizing;
    className = `${className} ${ls}`;
  }

  if (!text) {
    return `<p class="${className}"></p>`;
  }
  

  if (text.length > 1) {
    return blocks
    .map(block => block.children.map(child => child.text).join(''));
  }

  let bt = toHTML({ blocks: text });
  bt = bt.replace(/<p/, `<p class="${className}" `);
  return bt;
};

export default BodyText;

Oct 12, 2022, 9:49 PM
Could you please try commenting out the third if statement and block (text.length &gt; 1) and removing the destructuring in the
let bt
line (so
let bt = toHTML(text)
)?
Oct 12, 2022, 10:13 PM
k, but that is removing the return statement that displays anything
Oct 12, 2022, 10:45 PM
so now I get nothing
Oct 12, 2022, 10:45 PM
Well, technically I get
<p class="body-text col-6"></p>
Oct 12, 2022, 10:47 PM
When those lines aren't commented I get this:Unknown block type "undefined", specify a component for it in the
components.types
option
Oct 12, 2022, 10:48 PM
I see. I had tried implementing a pared down version of what you posted, so perhaps I misunderstood your code.
Where is
blocks
coming from in this?

if (text.length > 1) {
  return blocks
  .map(block => block.children.map(child => child.text).join(''));
}

Oct 12, 2022, 10:48 PM
So, that code is a bit of a mess becuase I was starting to replace the old code - and in doing so probably made mistakes. Here is the original code with the old blocks to html:

import blocksToHtml from '@sanity/block-content-to-html';

const BodyText = ({ text, layoutSettings } = {}) => {
  let className = 'body-text';
  let ls = 'col-6';
  if (layoutSettings?.sizing) {
    ls = layoutSettings.sizing;
    className = `${className} ${ls}`;
  }

  if (!text) {
    return `<p class="${className}"></p>`;
  }

  if (text.length > 1) {
    return blocksToHtml({
      blocks: text,
      className: className,
    });
  }

  let bt = blocksToHtml({ blocks: text });
  bt = bt.replace(/<p/, `<p class="${className}" `);
  return bt;
};

export default BodyText;
Oct 12, 2022, 10:51 PM
Oh, and I was looking at this info and copied and pasted that.. but looking at it now, I am realizing what I forgot. Regardless, I am still at a loss
Oct 12, 2022, 10:53 PM
I think the new @portabletext/to-html package will correctly handle whether there’s 1 or 1+ blocks (could be wrong, though), but if it doesn’t that should throw a different error.
[NB:
☝️This relates to whether or not you need
if (text.length > 1) {...}
.]
To go back to earlier, can you
console.log(text)
in your BodyText component? If it’s undefined, then your return makes sense, given:

if (!text) {
  return `<p class="${className}"></p>`;
}
Oct 12, 2022, 10:57 PM
For some reason, nothing is showing up in the console no matter where i put the console.log and what I put in it
Oct 12, 2022, 11:04 PM
Hi Stevey. I just tested the following in a React app and it rendered the HTML. Hopefully it can be a starting point for your migration from the old package to the new one.

import {useEffect, useState} from 'react';
import {toHTML} from '@portabletext/to-html';
import configuredClient from './client';

export default function BodyText() {
	const [pt, setPt] = useState();

	useEffect(() => {
		client.fetch(`*[_id == '92fae10c-3209-4b28-bedd-94df0df526c3'].body[]`)
			.then(res => setPt(res))
	}, [])	
	
	return pt && (
		<div>{toHTML(pt)}</div>
	)
}
Oct 13, 2022, 1:49 AM
user A
Thank you so much for that, it definitely helped! I was out yesterday, so I didn't get a chance to mess with it until this morning. Here is the code that worked for me (in case anyone looks at this thread)

import {toHTML} from '@portabletext/to-html';

const BodyText = ({ text, layoutSettings } = {}) => {
  let className = 'body-text';
  let ls = 'col-6';
  if (layoutSettings?.sizing) {
    ls = layoutSettings.sizing;
    className = `${className} ${ls}`;
  }

  if (!text) {
    return `<p class="${className}"></p>`;
  }


  if (text.length > 1) {
    return toHTML(text);
  }

  let bt = toHTML(text);
  bt = bt.replace(/<p/, `<p class="${className}" `);
  return bt;
};

export default BodyText;
Oct 14, 2022, 2:18 PM
You’re welcome! Thank you for following up with your final code, which is admittedly more relevant to someone using that package than my React snippet. Glad you got this working; have a great weekend!
Oct 14, 2022, 2:20 PM

Sanity– build remarkable experiences at scale

Sanity is a modern headless CMS that treats content as data to power your digital business. Free to get started, and pay-as-you-go on all plans.

Was this answer helpful?