Image Serializer Error: Works on Navigation but Fails on Direct Link

11 replies
Last updated: Dec 15, 2022
Hi! I need help with serializer for images on block content. It is working when I navigate the app (eg, https://rednacionaldemujeres.org --> Noticias --> 2.d item, it renders the images properly), but if I use the diret link to the item (eg, https://rednacionaldemujeres.org/noticias/mujeres-paz-y-seguridad-entre-luces-y-sombras-mujeres-rurales-santandereanas ), it throws an error ("Cannot stringify a function image"). I don't understand whats going on. Any thoughts?
AI Update

This is a classic Next.js serialization issue! The error "Cannot stringify a function image" occurs because you're likely passing your Portable Text serializers/components through Next.js data-fetching methods like getStaticProps or getServerSideProps, which can only return JSON-serializable data.

The Problem:

When you navigate within your app (client-side routing), the serializers work fine because they're already loaded in the browser as JavaScript functions. But when you access the page directly via URL (server-side rendering), Next.js tries to serialize the props returned from getStaticProps/getServerSideProps, and functions (like your image serializer) cannot be serialized to JSON.

The Solution:

Move your Portable Text component configuration to the client-side component rather than passing it through your data-fetching functions. Here's how:

❌ Don't do this:

// pages/noticias/[slug].js
export async function getStaticProps({ params }) {
  const post = await client.fetch(query, { slug: params.slug })
  
  return {
    props: {
      post,
      components: {  // ❌ Can't serialize functions!
        types: {
          image: MyImageComponent
        }
      }
    }
  }
}

✅ Do this instead:

// pages/noticias/[slug].js
import { PortableText } from '@portabletext/react'
import imageUrlBuilder from '@sanity/image-url'

const builder = imageUrlBuilder(client)

// Define components in the component file, not in getStaticProps
const components = {
  types: {
    image: ({ value }) => (
      <img
        src={builder.image(value).url()}
        alt={value.alt || ''}
      />
    )
  }
}

export default function NoticiaPage({ post }) {
  return (
    <article>
      <PortableText 
        value={post.body} 
        components={components}  // ✅ Defined client-side
      />
    </article>
  )
}

export async function getStaticProps({ params }) {
  const post = await client.fetch(query, { slug: params.slug })
  
  return {
    props: {
      post  // ✅ Only serializable data
    }
  }
}

Key Points:

  1. Only pass plain data (strings, numbers, arrays, objects) through getStaticProps/getServerSideProps
  2. Define your Portable Text components/serializers in your React component, not in the props
  3. The image data from Sanity (references, URLs, etc.) is serializable—it's the component functions that aren't

If you're using the older block-content-to-react or custom serializers, the same principle applies: keep the serializer functions in your component code, not in your data-fetching returns.

This is a Next.js limitation - the devalue library they use for serialization cannot handle functions, which is why you see this error specifically on direct page loads but not on client-side navigation.

Show original thread
11 replies
I'm using this https://v0.sanity.nuxtjs.org/helpers/portable-text
And the serializer component is just this line:
SanityImage(:asset-id="asset._ref" w="660").img
Can you share your relevant code?
Thanks, RD:-- MainPage.vue

// Template
SanityContent(:blocks="data.contenido" :serializers="serializers")

// Script
data: () => ({
  data: '',
  serializers: {
    types: {
      image: () =>
     import('~/components/imagen.vue'),
         }
      }}),
Component "imagen.vue"

<template lang="pug">
SanityImage(:asset-id="asset._ref" w="660")
</template>

<script>
export default {
  props: ['asset']
}
</script>

Maybe this is relevant: [vue-router] &lt;router-link&gt;'s event prop is deprecated and has been removed in Vue Router 4. Use the v-slot API to remove this warning: https://next.router.vuejs.org/guide/migration/#removal-of-event-and-tag-props-in-router-link .
😢 I can't find the answer anywhere
I'm unfortunately not clear on what's causing it yet either 😞
Oh, thanks for your answere. Is there any aditional information that I can provide you?
No, I think it's a matter of combing through your code to find what may be wrong. Unfortunately, my time's been super limited this week because of a high volume of support requests. I'll do my best, though!
Is there any other way to render images between blocks? That's the only reason why I'm using the serializer
Ohhh, I found the problem. I was fetching Sanity with "async fetch()" instead of "asyncData()", so I guess the fetching was not on server but on client side. I changed it and it worked.
Great! Thank you for sharing your solution!

Sanity – Build the way you think, not the way your CMS thinks

Sanity is the developer-first content operating system that gives you complete control. Schema-as-code, GROQ queries, and real-time APIs mean no more workarounds or waiting for deployments. Free to start, scale as you grow.

Was this answer helpful?