vibecheck0.3.0
Web essentialsweb-essentialscharsetwarning

Missing charset declaration in React

With no explicit <meta charset="utf-8">, the browser falls back to a guessed encoding and can render “café” as “café” or drop emoji entirely. Worse, if the tag appears late in a large <head>, the browser may have to restart parsing. It is invisible in dev on modern servers that send a charset header, then breaks on a static host that does not.

Symptoms

  • Accented characters and emoji render as garbled “mojibake”
  • Content looks fine locally but breaks when served from a CDN or file://
  • The browser restarts HTML parsing after discovering the charset late

How VibeCheck catches it

In your widget · Problems

warningweb-essentialsMissing charset declaration

To your coding agent · MCP

agent › get_detected_issues
{ detector: "web-essentials", issue: "Missing charset declaration", threshold: "No element matching <meta charset> in the document head" }

The same string in your widget and in your agent’s context — no screenshot, no copy-paste.

Root causes

  • A hand-written HTML shell omitted it
  • The charset meta was placed after other tags or scripts instead of first
  • Relying on the HTTP Content-Type charset header, which is absent on some hosts

The fix for React

Vite/CRA React apps declare it in index.html as the first head tag.

index.htmlhtml
<head>
  <meta charset="UTF-8" />
  <meta name="viewport" content="width=device-width, initial-scale=1" />
</head>

Steps

  1. Open your document head
  2. Add <meta charset="utf-8"> as the first child of <head>
  3. Verify accented text and emoji render correctly on a static host

See the general, framework-agnostic fix →

FAQ

Do I need the long <meta http-equiv="Content-Type"> form?
No. The short <meta charset="utf-8"> has been valid HTML5 for over a decade and is preferred. Keep it as the first tag in <head>.
It works locally — why does VibeCheck still flag it?
Your dev server may send a charset in the HTTP header, masking the missing tag. A static host or file:// open won’t, so declare it in the HTML to be safe everywhere.