• About
  • Blog
  • Tools
  • Case Studies
  • Contact

ContentWrap simplifies your Sanity CMS workflow

© Copyright 2025 ContentWrap. All Rights Reserved.

Work
  • About
  • Blog
  • Tools
  • Case Studies
  • Contact
Legal
  • Terms of Service
  • Privacy Policy
  • Cookie Policy
  1. Blog
  2. how to eliminate hidden and whitespace characters in sanity CMS
Sanity Setup
Bugs & Fixes
Sanity
May 17, 2025

How to Eliminate Hidden Characters and Whitespace Issues in Sanity CMS: Why stegaClean is Essential

Tamba Monrose
Founder of ContentWrap
how to remove hidden characters and zero width in sanity cms

In this article

  1. Introduction
  2. Understanding Stega Encoding
  3. The Real-World Impact
  4. How to Identify Hidden Characters
  5. Implementing stegaClean
  6. Common Issues and Troubleshooting
  7. Conclusion and Next Steps

Share this article

Have you ever spent hours debugging a Sanity CMS project only to discover that invisible characters were breaking your conditional logic? Or found that string comparisons mysteriously fail despite visually identical text? You're not alone. Sanity's Visual Editing features, while powerful, introduce hidden metadata characters that can wreak havoc on your code.

Introduction

Sanity CMS is an exceptional headless content management system that powers websites for companies like Ramp, who recently migrated from Webflow to Sanity for their expanding content needs. However, one particularly frustrating challenge lurks beneath the surface: stega encoding – invisible characters that Sanity embeds in strings to support its Visual Editing features.

These hidden characters cause string comparisons to fail, break conditional rendering, and create maddening bugs that seem impossible to track down. The symptoms are subtle but the impact is severe: components render incorrectly, CSS classes don't apply as expected, and equality checks fail for seemingly identical strings.

Consider this real-world example from a recent project:

// This conditional mysteriously fails despite text appearing identical{pageType === "blog" ? <BlogHeader /> : <StandardHeader />}

By the end of this tutorial, you'll be able to:

  • Identify when stega encoding is causing issues in your Sanity projects
  • Properly implement stegaClean to eliminate these hidden characters
  • Debug string comparison problems with confidence
  • Create utility functions to handle stega cleaning systematically
  • Prevent future headaches with best practices for Sanity string handling

Prerequisites

  • Basic knowledge of Sanity CMS
  • Experience with Next.js or similar frontend frameworks
  • Familiarity with JavaScript string operations
  • A Sanity project with Visual Editing features

Understanding Stega Encoding

What Is Stega Encoding and Why Does It Exist?

Stega encoding (short for steganography encoding) is a technique Sanity uses to embed metadata within text strings. This metadata supports Sanity's Visual Editing features, allowing content creators to see real-time previews and enabling developers to build sophisticated editing experiences.

While this technology powers Sanity's excellent visual editing capabilities, it introduces invisible characters into your strings that can cause unexpected behavior in your frontend code.

The Hidden Characters Lurking in Your Content

These hidden characters include:

  • ZeroWidthSpace (&ZeroWidthSpace; or Unicode U+200B)
  • Zero Width Joiner (&zwj; or Unicode U+200D)
  • Zero Width Non-Joiner (&zwnj; or Unicode U+200C)
  • Byte Order Mark (&#xFEFF; or Unicode U+FEFF)

What makes these characters particularly problematic is that they're invisible in normal text display but are very much present in the string data. This means code that looks correct can behave unexpectedly.

Here's what a heading might actually look like in your HTML when inspected, with this example taken from ContentWrap's blog post about adding superscripts and subscripts in Sanity:

white space and zero width characters appearing in Sanity CMS preview mode
<h1 class="mt-2 text-4xl font-extrabold tracking-tight lg:text-5xl">  How to Add Superscripts and Subscripts to Sanity CMS: A Step-by-Step Guide&ZeroWidthSpace;&ZeroWidthSpace;&ZeroWidthSpace;&ZeroWidthSpace;&zwnj;&#xFEFF;&zwj;&#xFEFF;&ZeroWidthSpace;&zwj;&ZeroWidthSpace;&zwj;&zwnj;&zwj;&#xFEFF;&#xFEFF;&zwnj;&#xFEFF;&ZeroWidthSpace;&zwj;&zwnj;[...]</h1>

Notice all those extra character entities after the visible text? Those are stega characters that will break string comparisons and equality checks.

Before and After stegaClean

Here's a comparison of a string value before and after applying stegaClean:

Before:

console.log(title); // Output: "Product Features"// Note: The hidden characters aren't visible in console output but are present

After:

import { stegaClean } from '@sanity/client/stega'console.log(stegaClean(title));// Output: "Product Features"// Clean, without any hidden characters

The Real-World Impact

When Equality Checks Mysteriously Fail

One of the most common issues with stega encoding is equality checks that should work but don't:

// This will often fail despite appearing correctif (category === "Technology") {  // This code never executes even when category looks like "Technology"}// The solutionimport { stegaClean } from '@sanity/client/stega'if (stegaClean(category) === "Technology") {  // Now this works correctly}

CSS Classes Don't Apply as Expected

Another frequent issue occurs when dynamic class names don't apply:

// Before: Class won't apply due to hidden characters<div className={`card ${type === "featured" ? "card-featured" : ""}`}>// After: stegaClean ensures proper class application<div className={`card ${stegaClean(type) === "featured" ? "card-featured" : ""}`}>

Component Rendering Issues

Conditional rendering often breaks with stega-encoded strings:

// Before: Wrong component renders despite correct-looking value{contentType === "blog"   ? <BlogLayout />   : <StandardLayout />}// After: Reliable rendering with stegaClean{stegaClean(contentType) === "blog"   ? <BlogLayout />   : <StandardLayout />}

How to Identify Hidden Characters

Using Browser Developer Tools

The most direct way to detect these hidden characters is through browser developer tools:

  1. Right-click on the suspected element and select "Inspect"
  2. Look at the HTML in the Elements panel
  3. Select the element's text content
  4. Look for unusual spacing or cursor "jumps" when moving through the text

Programmatic Detection

You can detect hidden characters programmatically:

function hasStegaCharacters(str) {  const stegaRegex = /[\u200B\u200C\u200D\uFEFF]/g;  return stegaRegex.test(str);}// Usageconst textFromSanity = "Product Features";console.log(hasStegaCharacters(textFromSanity)); // true

Character Visualization Techniques

To visualize these characters:

function revealHiddenCharacters(str) {  return str.replace(/\u200B/g, "[ZWS]")            .replace(/\u200C/g, "[ZWNJ]")            .replace(/\u200D/g, "[ZWJ]")            .replace(/\uFEFF/g, "[BOM]");}console.log(revealHiddenCharacters(textFromSanity));// Output: "Product Features[ZWJ][ZWNJ][BOM][ZWJ]"

Character Code Inspection

For deeper inspection:

function inspectStringCodes(str) {  return Array.from(str).map(char => ({    char,    code: char.charCodeAt(0),    hex: `U+${char.charCodeAt(0).toString(16).toUpperCase().padStart(4, '0')}`,    name: getCharacterName(char.charCodeAt(0))  }));}// Example character names (simplified)function getCharacterName(code) {  switch(code) {    case 0x200B: return "ZERO WIDTH SPACE";    case 0x200C: return "ZERO WIDTH NON-JOINER";    case 0x200D: return "ZERO WIDTH JOINER";    case 0xFEFF: return "BYTE ORDER MARK";    default: return "VISIBLE CHARACTER";  }}console.table(inspectStringCodes(textFromSanity));// Outputs a table with character details

Implementing stegaClean

Step 1: Install the Required Package

If you're using the latest Sanity client, stegaClean is included. Otherwise, install it:

npm install @sanity/client# oryarn add @sanity/client

Step 2: Import stegaClean

// Import stegaClean from the client libraryimport { stegaClean } from '@sanity/client/stega'

Step 3: Apply stegaClean at the Right Points

Apply stegaClean at these critical points:

1. String Comparisons

if (stegaClean(category) === "Technology") {  // Logic here}

2. Class Name Generation

3. Before Using String Methods

const words = stegaClean(title).split(' ');const hasKeyword = stegaClean(content).includes('important phrase');

4. When Passing Props to Child Components

<CustomComponent   title={stegaClean(data.title)}  description={stegaClean(data.description)}/>

Step 4: Create a Reusable Utility

For systematic application:

// utils/sanityHelpers.jsimport { stegaClean } from '@sanity/client/stega'export function cleanString(str) {  if (typeof str !== 'string') {    return str;  }  return stegaClean(str);}export function cleanObject(obj) {  if (!obj || typeof obj !== 'object') {    return obj;  }    if (Array.isArray(obj)) {    return obj.map(item => cleanObject(item));  }    return Object.fromEntries(    Object.entries(obj).map(([key, value]) => {      if (typeof value === 'string') {        return [key, cleanString(value)];      }      if (typeof value === 'object' && value !== null) {        return [key, cleanObject(value)];      }      return [key, value];    })  );}

Step 5: Integrate with Higher-Order Components (Advanced)

For React applications, create a HOC:

// components/withCleanProps.jsximport React from 'react';import { cleanObject } from '../utils/sanityHelpers';export function withCleanProps(Component) {  return function CleanPropsWrapper(props) {    const cleanProps = cleanObject(props);    return <Component {...cleanProps} />;  };}// Usageconst CleanBlogPost = withCleanProps(BlogPost);

Common Issues and Troubleshooting

Problem: Equality Checks Still Failing

Symptoms:

  • if conditions not executing as expected
  • Filters not including expected items
  • Component props differing from expected values

Solution:

1. Ensure you're applying stegaClean directly in the comparison:

// Correctif (stegaClean(value) === "expected") {}// Incorrect - cleaning too lateif (value === "expected") {} // stegaClean applied elsewhere

2. Check for case sensitivity issues:

// More robust solutionif (stegaClean(value).toLowerCase() === "expected".toLowerCase()) {}

Problem: Missing stegaClean in Nested Objects

Symptoms:

  • Some comparisons work, others don't
  • Inconsistent behavior across components

Solution: Use the cleanObject utility we created:

// Beforeconst post = {  title: sanityData.title, // Has stega characters  description: sanityData.description, // Has stega characters  categories: sanityData.categories // Array with stega characters};// Afterimport { cleanObject } from '../utils/sanityHelpers';const post = cleanObject(sanityData);

Problem: Cleaning Values That Don't Need It

Symptoms:

  • Unnecessary performance overhead
  • Applying stegaClean to non-Sanity strings

Solution: Only apply stegaClean to strings from Sanity:

// Helper function to determine if cleaning is neededfunction needsCleaning(str) {  if (typeof str !== 'string') return false;  // Check for common stega characters  return /[\u200B\u200C\u200D\uFEFF]/.test(str);}// Optimized cleaning functionfunction smartClean(str) {  if (!needsCleaning(str)) return str;  return stegaClean(str);}

Problem: stegaClean Missing from Build

Symptoms:

  • Works in development but fails in production
  • Import errors

Solution: Ensure proper import path and check bundling:

// Try alternative import paths if neededimport { stegaClean } from '@sanity/client/stega'// orimport { stegaClean } from '@sanity/client/dist/stega'// Create a fallback if neededfunction safeStegaClean(str) {  try {    return stegaClean(str);  } catch (e) {    console.warn('stegaClean failed, returning original string', e);    return str;  }}

Problem: Hidden Characters in URL Slugs

Symptoms:

  • Broken links
  • 404 errors on seemingly correct URLs

Solution: Always clean slugs:

// In your getStaticPaths or route handlerexport async function getStaticPaths() {  const posts = await client.fetch(`*[_type == "post"]{ slug }`);    return {    paths: posts.map(post => ({      params: {         slug: stegaClean(post.slug.current)      }    })),    fallback: false  };}

Conclusion and Next Steps

Stega encoding is a necessary component of Sanity's powerful Visual Editing features, but the hidden characters it introduces can cause frustrating bugs in your applications. By systematically implementing stegaClean, you can eliminate these issues while still benefiting from Sanity's excellent editing experience.

Let's recap what we've learned:

  1. Sanity embeds invisible characters like ZeroWidthSpace, ZWJ, ZWNJ, and BOM in strings
  2. These characters break equality checks, conditional rendering, and string operations
  3. stegaClean removes these characters, making strings behave as expected
  4. Implementing stegaClean systematically through utilities and HOCs ensures consistent behavior

Next Steps

To further enhance your Sanity CMS development experience:

  1. Create a stegaClean Linting Rule: Build an ESLint rule to flag potential stega issues
  2. Automate Testing: Add tests specifically for stega character handling
  3. Explore Sanity's Presentation Tool: Learn more about Visual Editing capabilities
  4. Contribute: Share your stegaClean utilities with the Sanity community

Additional Resources

  • Sanity Official Documentation on stegaClean
  • Understanding Unicode and Zero-Width Characters
  • Handling CMS Content in Next.js Applications
  • ContentWrap's Guide to Sanity Project Structure

Related Articles

adding open in new tab option to links in sanity cms
Sanity Setup
Custom Features
Sanity
May 2025
How to Add 'Open in New Tab' Option for Links in Sanity CMS: A Step-by-Step Guide

Learn how to add the missing "Open in New Tab" option to Sanity CMS links. This step-by-step guide shows you how to customize link annotations and properly implement frontend rendering for better content management.

how to implement text wrap around images in sanity cms
Sanity Setup
Rich Content & Embeds
Custom Features
Sanity
May 2025
How to Implement Text Wrap Around Images in Sanity CMS: A Step-by-Step Guide

Frustrated by Sanity's inability to wrap text around images? This tutorial shows you how to build a custom component that gives your content editors the power to float images left or right with adjustable width controls — just like Webflow.

superscript and subscript decorator options showing in decorators section of Sanity block array field
Sanity Setup
Custom Features
Sanity
April 2025
How to Add Superscripts and Subscripts to Sanity CMS: A Step-by-Step Guide

In this tutorial, you will learn how to extend Sanity CMS with custom superscript and subscript decorators for block array fields.