htmlSerializer doesn't seem to do anything

I have used the docs to create an htmlSerializer, but even when I hard-code content, it doesn't appear to change the output at all. Is there a step I'm missing? If I misspell the link to htmlSerializer in my Slice, it throws an error, so I know it's resolving the correct path.

What I'm working with currently (for testing):

import React from 'react';

import { RichText, Elements } from 'prismic-reactjs';

import ReactPlayer from 'react-player/lazy'

// -- Function to add unique key to props

const propsWithUniqueKey = function(props, key) {

  return Object.assign(props || {}, { key });

};

// -- HTML Serializer

const prismicSerializer = function(type, element, content, children, key) {

  var props = {};

  switch(type) {

      

    case Elements.heading1: // Heading 1

      return React.createElement('h1', propsWithUniqueKey(props, key), children);

      

    case Elements.heading2: // Heading 2

      return React.createElement('h2', propsWithUniqueKey(props, key), children);

      

    case Elements.heading3: // Heading 3

      return React.createElement('h3', propsWithUniqueKey(props, key), children);

      

    case Elements.heading4: // Heading 4

      return React.createElement('h4', propsWithUniqueKey(props, key), children);

      

    case Elements.heading5: // Heading 5

      return React.createElement('h5', propsWithUniqueKey(props, key), children);

      

    case Elements.heading6: // Heading 6

      return React.createElement('h6', propsWithUniqueKey(props, key), children);

      

    case Elements.paragraph: // Paragraph

      return <Typography variant="h3">{children}</Typography>

      

    case Elements.preformatted: // Preformatted

      return React.createElement('pre', propsWithUniqueKey(props, key), children);

      

    case Elements.strong: // Strong

      return React.createElement('strong', propsWithUniqueKey(props, key), children);

      

    case Elements.em: // Emphasis

      return React.createElement('em', propsWithUniqueKey(props, key), children);

      

    case Elements.listItem: // Unordered List Item

      return React.createElement('li', propsWithUniqueKey(props, key), children);

      

    case Elements.oListItem: // Ordered List Item

      return React.createElement('li', propsWithUniqueKey(props, key), children);

      

    case Elements.list: // Unordered List

      return React.createElement('ul', propsWithUniqueKey(props, key), children);

      

    case Elements.oList: // Ordered List

      return React.createElement('ol', propsWithUniqueKey(props, key), children);

      

    case Elements.image: // Image

      const linkUrl = element.linkTo ? element.linkTo.url || linkResolver(element.linkTo) : null;

      const linkTarget = (element.linkTo && element.linkTo.target) ? { target: element.linkTo.target } : {};

      const linkRel = linkTarget.target ? { rel: 'noopener' } : {};

      const img = React.createElement('img', { src: element.url , alt: element.alt || '' });

      return React.createElement(

        'p',

        propsWithUniqueKey({ className: [element.label || '', 'block-img'].join(' ') }, key),

        linkUrl ? React.createElement('a', Object.assign({ href: linkUrl }, linkTarget, linkRel), img) : img

      );

      

    case Elements.embed: // Embed

      return <ReactPlayer url="https://www.youtube.com/watch?v=9ulOEpbdF-o" />

      

    case Elements.hyperlink: // Image

      const targetAttr = element.data.target ? { target: element.data.target } : {};

      const relAttr = element.data.target ? { rel: 'noopener' } : {};

      props = Object.assign({ 

        href: element.data.url || linkResolver(element.data)

      }, targetAttr, relAttr);

      return React.createElement('a', propsWithUniqueKey(props, key), children);

      

    case Elements.label: // Label

      props = element.data ? Object.assign({}, { className: element.data.label }) : {};

      return React.createElement('span', propsWithUniqueKey(props, key), children);

      

    case Elements.span: // Span

      if (content) {

        return content.split("\n").reduce((acc, p) => {

          if (acc.length === 0) {

            return [p];

          } else {

            const brIndex = (acc.length + 1)/2 - 1;

            const br = React.createElement('br', propsWithUniqueKey({}, brIndex));

            return acc.concat([br, p]);

          }

        }, []);

      } else {

        return null;

      }

    default: // Always include a default that returns null

      return null;

  }

};

My slice:

const WideText = ({ slice }) => {

  const styles = theme()

  return(

    <Grid container component="section">

      <RichText render={slice.primary.text} htmlSerializer={prismicSerializer} />

    </Grid>

)}

export default WideText;

Hi Mark,

Thanks for reaching out.

In fact, it depends on the content that you have in the "slice.primary.text" that you are passing.

One way to figure out the issue is to add a log in the serializer function to see if it enters to this function and what is type of element you are passing.

And it will be also helpful to share with us your repository name (in a private message if necessary).

Looking forward to your reply,
Fares

I've tested with text components.

Yes I understand, have you tried to add logs? and still need to have your repository name to debug this issue.

Edit-elements

I've tried logging to no avail.

Any word on this? It's been almost a week.

Hi Mark,

What do you mean by "I've tried logging to no avail." you didn't get any logs in the console?

If you not getting anything in the logs when logging inside the prismicSerializer function then it means that you are not referring to it correctly.

The only way for me to debug this to have access to the project itself, let me know if that is possible.
Best

I gave you the repo: edit-elements. What I mean is if I reference htmlSerializer, it doesn't throw an error. If I change the name in the reference, it throws an error, so I know that it's referencing it correctly. It's just not logging anything no matter what I put in the htmlSerializer switch block. Maybe there's a misconfig somewhere? Let me know what I need to do to give you access to the repo on Prismic's end, and if you need access to the frontend repo, I can do that as well: I'll post one slice so you can see what I'm doing, and maybe I'm doing it wrong:

WideText Slice:

import React from 'react'
import { RichText } from 'prismic-reactjs'
import { Grid, makeStyles } from '@material-ui/core'
import { prismicSerializer } from '../../prismic-serializer'

const theme = makeStyles({
  textBlock: {
    marginBottom: '2rem'
  }
})

const WideText = ({ slice }) => {
  const styles = theme()
  return(
    <Grid container component="section">
      <RichText render={slice.primary.text} htmlSerializer={prismicSerializer} />
    </Grid>
)}

export default WideText;

My prismic serializer file:

import React from 'react';
import { RichText, Elements } from 'prismic-reactjs';
import ReactPlayer from 'react-player/lazy'

// -- Function to add unique key to props
const propsWithUniqueKey = function(props, key) {
  return Object.assign(props || {}, { key });
};

// -- HTML Serializer
const prismicSerializer = function(type, element, content, children, key) {
  console.log('serializer props', type, element, content, children, key)

  var props = {};

  switch(type) {
      
    case Elements.heading1: // Heading 1
      return React.createElement('h1', propsWithUniqueKey(props, key), children);
      
    case Elements.heading2: // Heading 2
      return React.createElement('h2', propsWithUniqueKey(props, key), children);
      
    case Elements.heading3: // Heading 3
      return React.createElement('h3', propsWithUniqueKey(props, key), children);
      
    case Elements.heading4: // Heading 4
      return React.createElement('h4', propsWithUniqueKey(props, key), children);
      
    case Elements.heading5: // Heading 5
      return React.createElement('h5', propsWithUniqueKey(props, key), children);
      
    case Elements.heading6: // Heading 6
      return React.createElement('h6', propsWithUniqueKey(props, key), children);
      
    case Elements.paragraph: // Paragraph
      return <Typography variant="h3">{children}</Typography>
      
    case Elements.preformatted: // Preformatted
      return React.createElement('pre', propsWithUniqueKey(props, key), children);
      
    case Elements.strong: // Strong
      return React.createElement('strong', propsWithUniqueKey(props, key), children);
      
    case Elements.em: // Emphasis
      return React.createElement('em', propsWithUniqueKey(props, key), children);
      
    case Elements.listItem: // Unordered List Item
      return React.createElement('li', propsWithUniqueKey(props, key), children);
      
    case Elements.oListItem: // Ordered List Item
      return React.createElement('li', propsWithUniqueKey(props, key), children);
      
    case Elements.list: // Unordered List
      return React.createElement('ul', propsWithUniqueKey(props, key), children);
      
    case Elements.oList: // Ordered List
      return React.createElement('ol', propsWithUniqueKey(props, key), children);
      
    case Elements.image: // Image
      const linkUrl = element.linkTo ? element.linkTo.url || linkResolver(element.linkTo) : null;
      const linkTarget = (element.linkTo && element.linkTo.target) ? { target: element.linkTo.target } : {};
      const linkRel = linkTarget.target ? { rel: 'noopener' } : {};
      const img = React.createElement('img', { src: element.url , alt: element.alt || '' });
      return React.createElement(
        'p',
        propsWithUniqueKey({ className: [element.label || '', 'block-img'].join(' ') }, key),
        linkUrl ? React.createElement('a', Object.assign({ href: linkUrl }, linkTarget, linkRel), img) : img
      );
      
    case Elements.embed: // Embed
      console.log('embed')
      return <ReactPlayer url="https://www.youtube.com/watch?v=9ulOEpbdF-o" />
      
    case Elements.hyperlink: // Image
      const targetAttr = element.data.target ? { target: element.data.target } : {};
      const relAttr = element.data.target ? { rel: 'noopener' } : {};
      props = Object.assign({ 
        href: element.data.url || linkResolver(element.data)
      }, targetAttr, relAttr);
      return React.createElement('a', propsWithUniqueKey(props, key), children);
      
    case Elements.label: // Label
      props = element.data ? Object.assign({}, { className: element.data.label }) : {};
      return React.createElement('span', propsWithUniqueKey(props, key), children);
      
    case Elements.span: // Span
      if (content) {
        return content.split("\n").reduce((acc, p) => {
          if (acc.length === 0) {
            return [p];
          } else {
            const brIndex = (acc.length + 1)/2 - 1;
            const br = React.createElement('br', propsWithUniqueKey({}, brIndex));
            return acc.concat([br, p]);
          }
        }, []);
      } else {
        return null;
      }

    default: // Always include a default that returns null
      return null;
  }
};

A product page that uses SliceZone:

import { client } from '../../../prismic-configuration'
import { Divider, Grid, Typography, makeStyles } from '@material-ui/core'
import SliceZone from 'next-slicezone'
import { useGetStaticPaths } from 'next-slicezone/hooks'
import SliceResolver from '../../../sm-resolver'
import AddToCartButton from '../../../components/AddToCartButton'
import { useEffect, useState } from 'react'

const theme = makeStyles({
  'section': {
    margin: '3rem auto'
  },
  outer: {
    padding: '1rem .5rem'
  },
  container: {
    margin: 'auto',
  },
  slim: {
    margin: 'auto',
    maxWidth: '1200px',
  },
  productImg: {
    width: '100%',
    height: 'auto',
    maxWidth: '500px'
  },
  productTitle: {
    marginTop: '3rem'
  },
  justifyText: {
    textAlign: 'justify'
  },
  wideDivide: {
    margin: '3em auto'
  },
  strike: {
    textDecoration: 'line-through',
    fontSize: '1.5rem',
    color: '#999999'
  }
})

const CategoryProduct = (props) => {
  const [ currentProduct, setCurrentProduct ] = useState(null)

  useEffect(() => {
    const { product } = props
    setCurrentProduct(prevState => product)
  }, [currentProduct])

  const product = props.product
  const slices = product.data.body
  const { featured_image, price, on_sale, sale_price, title, short_description } = product.data
  const styles = theme()
  return(
    <Grid container direction="column" mt={6} className={styles.outer}>
      <Grid container style={{marginBottom: '6rem'}} className={styles.slim}>
        <Grid item xs={12} md={4}>
          <img className={styles.productImg} src={featured_image.medium.url} />
        </Grid>
        <Grid item sm={12} md={8} sx={{textAlign: 'right'}}>
          <Typography className={styles.productTitle} variant="h1">{title[0].text}</Typography>
          <Typography mb={2} variant="h4">{on_sale && sale_price ? <div><span className={styles.strike}>${price}</span>&nbsp;${sale_price}</div> : `$${price}`}</Typography>
          <AddToCartButton product={product} />
        </Grid>
        <Grid container justifyContent="flex-end">
          <Grid item xs={12} md={6} textAlign="justify">
            <Divider className={styles.wideDivide} />
            <Typography className={styles.justifyText} sx={{maxWidth: '600px', margin: 'auto'}}>{short_description[0].text}</Typography>          
          </Grid>          
        </Grid>
      </Grid>
      <Grid container direction="column" className={[styles.slices, styles.slim].join(' ')}>
        <SliceZone resolver={SliceResolver} slices={slices} />
      </Grid>
    </Grid>
  )
}

export const getStaticProps = async ({params}) => {
  const product = await client.getByUID('product', params.uid)
  const { price, sale_price } = product.data
  // Where we return the specific product's data
  return {
    props: {
      product
    }
  }
}

export const getStaticPaths = useGetStaticPaths({
  client: client,
  type: 'product',
  formatPath: (doc) => {
    // Where we return paths for /category/uid for static pages
    return {
      params: {
        category: doc.data.category.uid,
        uid: doc.uid 
      }
    }
  }
})
export default CategoryProduct

Let me know if there's anything else you need. :slight_smile:

Any updates? If there's something else you need, just let me know. :slight_smile:

Hi @mark
I'm still not seeing the issue, I would need your frontend code repository to debug it.

I sent you an invite to my GitLab project. :slight_smile:

Ok, perfect I see it, I will have a look and get back to you.

1 Like

Awesome, thank you! :+1:

Any word? I still haven't seen anything yet. Thanks!

Hi Mark,

Fares is out so I'm taking over this one for you.

Just having a look at your serializer I'm not sure I understand your use case. It looks like you are passing a youtube video directly into the HTML serializer.

This isn't the intended use, the HTML serializer basically takes the formatted text from the Prismic Rich Text field and converts it to HTML. So you could assign classes to every H1 tag on your website for example.

Can you tell me a little bit more about what you're trying to do. I see you were discussing embed dimensions in another thread. Is this related?

Thanks.

The problem is two-fold.

  1. I would like to specify (or omit) dimensions to generate an embed code that is responsive.
  2. I am using Material UI, and I am trying to wrap the elements in the serializer with MUI components.

Any word? Even passing the normal default values doesn't work when I change out even some of the words, leaving the rest of the logic in place. I gave Fares access to my repo to take a look at it, should I grant someone else access as well?

Hey @mark,

Sorry for the delay. Phil is out sick this week, so I'm taking over. Could you invite me to your git repo? And, to clarify, is it all elements that aren't working in the htmlSerializer — or only the embed element?

Thanks,
Sam

Hey @mark,

Thanks for your patience while I dug into this :slight_smile:

I'm not sure, but it looks like you're not exporting anything from prismic-serializer.js. I changed line 11 to:

export const prismicSerializer = function (

And the HTML Serializer registered, though then it started throwing errors.

Let me know if I'm misunderstanding, though, and I'm happy to keep debugging!

Sam

To be honest, the documentation (as least when I consulted it) was a bit fragmented. I'll give you a high-level idea of what my thought process is and maybe we can work from there. I appreciate you taking a look into this.

I want my editor to be able to add a Product with various slicezone items. The slicezone uses Material-UI locally. I push those into Prismic. When the [uid] page is rendered, it should pass the slices through the serializer, which is where the MUI components wrap the actual data coming from the slices. Does that make sense?

However we can make that work is totally fine with me. I just want to use <Typography> and <Grid> elements inside of the slices. Please let me know the best way to accommodate that. :slight_smile: