<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Fazeel Mehar</title>
    <description>The latest articles on DEV Community by Fazeel Mehar (@fazeelmehar).</description>
    <link>https://dev.to/fazeelmehar</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F3930181%2Fb77563bb-d14b-4f05-ad4c-49cc9b9a26e0.jpg</url>
      <title>DEV Community: Fazeel Mehar</title>
      <link>https://dev.to/fazeelmehar</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/fazeelmehar"/>
    <language>en</language>
    <item>
      <title>I built a free JSON Formatter &amp; Validator with Angular 21 and Monaco Editor</title>
      <dc:creator>Fazeel Mehar</dc:creator>
      <pubDate>Wed, 13 May 2026 22:33:39 +0000</pubDate>
      <link>https://dev.to/fazeelmehar/i-built-a-free-json-formatter-validator-with-angular-21-and-monaco-editor-2ag8</link>
      <guid>https://dev.to/fazeelmehar/i-built-a-free-json-formatter-validator-with-angular-21-and-monaco-editor-2ag8</guid>
      <description>&lt;p&gt;Working with JSON every day, I found myself constantly switching between browser tabs to format messy payloads from APIs. Every tool I used either had ads everywhere, required signup, or sent my data to a server. So I built my own.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;a href="https://jsonproject.com" rel="noopener noreferrer"&gt;jsonproject.com&lt;/a&gt;&lt;/strong&gt; — a free, browser-only JSON formatter, validator, and auto-fixer. No ads, no signup, no data ever leaves your machine.&lt;/p&gt;

&lt;h2&gt;
  
  
  What it does
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Format &amp;amp; pretty-print&lt;/strong&gt; — 2, 3, 4 spaces, tabs, or compact mode&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Validate&lt;/strong&gt; against RFC 8259 with inline error markers&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Minify&lt;/strong&gt; for production payloads&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Auto-fix&lt;/strong&gt; broken JSON — trailing commas, single quotes, unquoted keys&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Fetch JSON from a URL&lt;/strong&gt; — paste an API endpoint and format the response instantly&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Drag-and-drop file upload&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Format history&lt;/strong&gt; — last 20 operations saved in session&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Dark mode&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Monaco Editor&lt;/strong&gt; — same editor as VS Code, with full syntax highlighting&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Tech stack
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Angular 21&lt;/strong&gt; with SSR and prerendering&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Monaco Editor&lt;/strong&gt; via &lt;code&gt;ngx-monaco-editor-v2&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Tailwind CSS&lt;/strong&gt; for styling&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;TypeScript&lt;/strong&gt; throughout&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  The interesting parts
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Monaco Editor in Angular SSR
&lt;/h3&gt;

&lt;p&gt;The trickiest part was getting Monaco Editor to work with Angular's server-side rendering. Monaco is a browser-only library — it directly accesses &lt;code&gt;window&lt;/code&gt;, &lt;code&gt;document&lt;/code&gt;, and the DOM. Running it on the server causes everything to crash.&lt;/p&gt;

&lt;p&gt;The fix: wrap the entire formatter component with &lt;code&gt;ngSkipHydration&lt;/code&gt; and guard all Monaco initialization behind &lt;code&gt;isPlatformBrowser()&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;isPlatformBrowser&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;platformId&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="c1"&gt;// Monaco-specific code here&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And in the template:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;app-formatter&lt;/span&gt; &lt;span class="na"&gt;ngSkipHydration&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;/app-formatter&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This lets Angular prerender the shell HTML on the server while Monaco initializes fully on the client.&lt;/p&gt;

&lt;h3&gt;
  
  
  Auto-fix engine
&lt;/h3&gt;

&lt;p&gt;The auto-fixer handles the most common JSON mistakes developers make:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="nf"&gt;attemptFix&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;input&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;s&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;input&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;trim&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
  &lt;span class="c1"&gt;// Remove trailing commas before } or ]&lt;/span&gt;
  &lt;span class="nx"&gt;s&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;s&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;replace&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sr"&gt;/,&lt;/span&gt;&lt;span class="se"&gt;(\s&lt;/span&gt;&lt;span class="sr"&gt;*&lt;/span&gt;&lt;span class="se"&gt;[&lt;/span&gt;&lt;span class="sr"&gt;}&lt;/span&gt;&lt;span class="se"&gt;\]])&lt;/span&gt;&lt;span class="sr"&gt;/g&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;$1&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="c1"&gt;// Convert single quotes to double quotes&lt;/span&gt;
  &lt;span class="nx"&gt;s&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;s&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;replace&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sr"&gt;/'&lt;/span&gt;&lt;span class="se"&gt;([^&lt;/span&gt;&lt;span class="sr"&gt;'&lt;/span&gt;&lt;span class="se"&gt;]&lt;/span&gt;&lt;span class="sr"&gt;*&lt;/span&gt;&lt;span class="se"&gt;)&lt;/span&gt;&lt;span class="sr"&gt;'&lt;/span&gt;&lt;span class="se"&gt;(\s&lt;/span&gt;&lt;span class="sr"&gt;*:&lt;/span&gt;&lt;span class="se"&gt;)&lt;/span&gt;&lt;span class="sr"&gt;/g&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;"$1"$2&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="nx"&gt;s&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;s&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;replace&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sr"&gt;/:&lt;/span&gt;&lt;span class="se"&gt;\s&lt;/span&gt;&lt;span class="sr"&gt;*'&lt;/span&gt;&lt;span class="se"&gt;([^&lt;/span&gt;&lt;span class="sr"&gt;'&lt;/span&gt;&lt;span class="se"&gt;]&lt;/span&gt;&lt;span class="sr"&gt;*&lt;/span&gt;&lt;span class="se"&gt;)&lt;/span&gt;&lt;span class="sr"&gt;'/g&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;: "$1"&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="c1"&gt;// Quote unquoted keys&lt;/span&gt;
  &lt;span class="nx"&gt;s&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;s&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;replace&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sr"&gt;/&lt;/span&gt;&lt;span class="se"&gt;([&lt;/span&gt;&lt;span class="sr"&gt;{,&lt;/span&gt;&lt;span class="se"&gt;]\s&lt;/span&gt;&lt;span class="sr"&gt;*&lt;/span&gt;&lt;span class="se"&gt;)([&lt;/span&gt;&lt;span class="sr"&gt;a-zA-Z_&lt;/span&gt;&lt;span class="se"&gt;][&lt;/span&gt;&lt;span class="sr"&gt;a-zA-Z0-9_&lt;/span&gt;&lt;span class="se"&gt;]&lt;/span&gt;&lt;span class="sr"&gt;*&lt;/span&gt;&lt;span class="se"&gt;)(\s&lt;/span&gt;&lt;span class="sr"&gt;*:&lt;/span&gt;&lt;span class="se"&gt;)&lt;/span&gt;&lt;span class="sr"&gt;/g&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;$1"$2"$3&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;s&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It's not a full parser — it uses targeted regex passes to handle the 90% case without breaking valid JSON.&lt;/p&gt;

&lt;h3&gt;
  
  
  Equal editor heights
&lt;/h3&gt;

&lt;p&gt;A subtle UX detail: making the input and output Monaco editors show exactly the same number of lines. Both use the same clamp height:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight css"&gt;&lt;code&gt;&lt;span class="nt"&gt;height&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="nt"&gt;clamp&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="err"&gt;240&lt;/span&gt;&lt;span class="nt"&gt;px&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="err"&gt;42&lt;/span&gt;&lt;span class="nt"&gt;vh&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="err"&gt;490&lt;/span&gt;&lt;span class="nt"&gt;px&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And critically — never use &lt;code&gt;flex-1&lt;/code&gt; on a Monaco editor. It sets &lt;code&gt;flex-basis: 0&lt;/code&gt; which overrides the explicit height and causes the editor to collapse on mobile.&lt;/p&gt;

&lt;h2&gt;
  
  
  Privacy
&lt;/h2&gt;

&lt;p&gt;All JSON processing is 100% client-side. The app is prerendered static HTML served from Hostinger. No backend, no database, no analytics on the JSON content. Google Analytics only tracks page views.&lt;/p&gt;

&lt;h2&gt;
  
  
  Try it
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;&lt;a href="https://jsonproject.com" rel="noopener noreferrer"&gt;jsonproject.com&lt;/a&gt;&lt;/strong&gt; — free, no signup, works on mobile too.&lt;/p&gt;

&lt;p&gt;Would love feedback — what features would you add? Drop a comment below.&lt;/p&gt;

</description>
      <category>angular</category>
      <category>javascript</category>
      <category>ai</category>
      <category>webdev</category>
    </item>
  </channel>
</rss>
