<?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>Forem: Emiliano Saurin</title>
    <description>The latest articles on Forem by Emiliano Saurin (@njoylab).</description>
    <link>https://forem.com/njoylab</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%2F3436939%2F07167ad8-1f28-4946-8bb0-085474557fd2.jpeg</url>
      <title>Forem: Emiliano Saurin</title>
      <link>https://forem.com/njoylab</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://forem.com/feed/njoylab"/>
    <language>en</language>
    <item>
      <title>I Built a JSON Fixer Because I Was Tired of Counting Characters</title>
      <dc:creator>Emiliano Saurin</dc:creator>
      <pubDate>Mon, 13 Apr 2026 21:05:00 +0000</pubDate>
      <link>https://forem.com/njoylab/i-built-a-json-fixer-because-i-was-tired-of-counting-characters-4ikb</link>
      <guid>https://forem.com/njoylab/i-built-a-json-fixer-because-i-was-tired-of-counting-characters-4ikb</guid>
      <description>&lt;p&gt;&lt;code&gt;JSON.parse()&lt;/code&gt; gives you an error at "position 42". Great. Position 42 of a 500-line config file. Let me just count characters manually, I guess.&lt;/p&gt;

&lt;p&gt;I hit this so often that at some point I stopped counting and built a &lt;a href="https://jsonlint.echovalue.dev/" rel="noopener noreferrer"&gt;JSON Linter&lt;/a&gt; that shows errors with actual line numbers. But more on that in a second. First, let's talk about why JSON keeps breaking in the same ways.&lt;/p&gt;

&lt;h2&gt;
  
  
  It's Always the Same Five Things
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Trailing commas.&lt;/strong&gt; You delete the last field, forget the comma on the one above it. Or you copy from JavaScript, where trailing commas are fine. JSON disagrees.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Alice"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"age"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;32&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Single quotes.&lt;/strong&gt; A Python habit. Or a "I'll just type this real quick" habit. JSON wants double quotes, always.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Comments.&lt;/strong&gt; JSON has no comments. None. This is honestly a spec failure. &lt;code&gt;tsconfig.json&lt;/code&gt; and VS Code settings use JSONC precisely because people need comments in config files. But standard JSON? Nope.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="err"&gt;//&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;this&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;breaks&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;everything&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"url"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"https://api.example.com"&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Unquoted keys.&lt;/strong&gt; Valid JavaScript, invalid JSON. &lt;code&gt;{ name: "Alice" }&lt;/code&gt; fails.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Missing brackets.&lt;/strong&gt; Usually from truncated logs or half-pasted API responses.&lt;/p&gt;

&lt;p&gt;These five cover maybe 90% of all JSON parse errors I've ever seen.&lt;/p&gt;

&lt;h2&gt;
  
  
  So I Built a Fixer
&lt;/h2&gt;

&lt;p&gt;The &lt;a href="https://jsonlint.echovalue.dev/" rel="noopener noreferrer"&gt;JSON Linter and Fixer&lt;/a&gt; does two things:&lt;/p&gt;

&lt;p&gt;Paste broken JSON, hit "Fix" and it auto-repairs trailing commas, single quotes, comments, unquoted keys. It uses &lt;a href="https://github.com/josdejong/jsonrepair" rel="noopener noreferrer"&gt;jsonrepair&lt;/a&gt; under the hood. It won't magically reconstruct corrupted data, but if the JSON is &lt;em&gt;almost&lt;/em&gt; right (and it usually is), it'll clean it up.&lt;/p&gt;

&lt;p&gt;Hit "Lint" and you get properly formatted output with syntax highlighting. Useful even when the JSON isn't broken, just minified into an unreadable blob.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nl"&gt;"users"&lt;/span&gt;&lt;span class="p"&gt;:[{&lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="s2"&gt;"Alice"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="nl"&gt;"age"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;32&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="nl"&gt;"addresses"&lt;/span&gt;&lt;span class="p"&gt;:[{&lt;/span&gt;&lt;span class="nl"&gt;"city"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="s2"&gt;"NYC"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="nl"&gt;"zip"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="s2"&gt;"10001"&lt;/span&gt;&lt;span class="p"&gt;}]},{&lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="s2"&gt;"Bob"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="nl"&gt;"age"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;25&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="nl"&gt;"addresses"&lt;/span&gt;&lt;span class="p"&gt;:[{&lt;/span&gt;&lt;span class="nl"&gt;"city"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="s2"&gt;"LA"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="nl"&gt;"zip"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="s2"&gt;"90001"&lt;/span&gt;&lt;span class="p"&gt;}]}]}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Nobody can read that. One click and it's formatted.&lt;/p&gt;

&lt;p&gt;Everything runs client-side. No backend, nothing uploaded anywhere.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Other Half: jq Without Installing Anything
&lt;/h2&gt;

&lt;p&gt;Once the JSON is valid, you usually need to pull something out of it. "Give me all the user names." "Which items have status &lt;code&gt;failed&lt;/code&gt;?" "Group these by category."&lt;/p&gt;

&lt;p&gt;You could write a quick script. Or you could use &lt;code&gt;jq&lt;/code&gt;, which does this kind of thing in one line. Except now you need to install &lt;code&gt;jq&lt;/code&gt;, and you're on a machine where you can't, or you don't feel like it.&lt;/p&gt;

&lt;p&gt;So the same site has a &lt;a href="https://jsonlint.echovalue.dev/jq-playground/" rel="noopener noreferrer"&gt;jq playground&lt;/a&gt;. Paste JSON, write a filter, see the result. Browser-only, same as the linter.&lt;/p&gt;

&lt;p&gt;Some examples that come up all the time, given this data:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Alice"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"age"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;32&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"role"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"admin"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"active"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Bob"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"age"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;25&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"role"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"user"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"active"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Carol"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"age"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;28&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"role"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"admin"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"active"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Pull all names:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nf"&gt;map&lt;/span&gt;&lt;span class="p"&gt;(.&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Filter active admins over 30:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="p"&gt;.[]&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="nf"&gt;select&lt;/span&gt;&lt;span class="p"&gt;(.&lt;/span&gt;&lt;span class="nx"&gt;role&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;admin&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="nx"&gt;and&lt;/span&gt; &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;age&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;30&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Reshape into a different format:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nf"&gt;map&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;isAdmin&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;(.&lt;/span&gt;&lt;span class="nx"&gt;role&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;admin&lt;/span&gt;&lt;span class="dl"&gt;"&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;Group by role and count:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nf"&gt;group_by&lt;/span&gt;&lt;span class="p"&gt;(.&lt;/span&gt;&lt;span class="nx"&gt;role&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="nf"&gt;map&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;role&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;.[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nx"&gt;role&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;count&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;length&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That last one gives you &lt;code&gt;[{"role": "admin", "count": 2}, {"role": "user", "count": 1}]&lt;/code&gt;. The kind of thing you'd normally open Python for.&lt;/p&gt;

&lt;p&gt;Other one-liners I use constantly:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="p"&gt;[.[]&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;category&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="nx"&gt;unique&lt;/span&gt;           &lt;span class="err"&gt;#&lt;/span&gt; &lt;span class="nx"&gt;unique&lt;/span&gt; &lt;span class="nx"&gt;values&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="nx"&gt;an&lt;/span&gt; &lt;span class="nx"&gt;array&lt;/span&gt;
&lt;span class="nf"&gt;max_by&lt;/span&gt;&lt;span class="p"&gt;(.&lt;/span&gt;&lt;span class="nx"&gt;price&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;                       &lt;span class="err"&gt;#&lt;/span&gt; &lt;span class="nx"&gt;most&lt;/span&gt; &lt;span class="nx"&gt;expensive&lt;/span&gt; &lt;span class="nx"&gt;item&lt;/span&gt;
&lt;span class="p"&gt;[.&lt;/span&gt;&lt;span class="nx"&gt;users&lt;/span&gt;&lt;span class="p"&gt;[].&lt;/span&gt;&lt;span class="nx"&gt;addresses&lt;/span&gt;&lt;span class="p"&gt;[].&lt;/span&gt;&lt;span class="nx"&gt;city&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="nx"&gt;unique&lt;/span&gt; &lt;span class="err"&gt;#&lt;/span&gt; &lt;span class="nx"&gt;flatten&lt;/span&gt; &lt;span class="nx"&gt;and&lt;/span&gt; &lt;span class="nx"&gt;dedupe&lt;/span&gt; &lt;span class="nx"&gt;nested&lt;/span&gt; &lt;span class="nx"&gt;arrays&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You get the idea. &lt;a href="https://jsonlint.echovalue.dev/jq-playground/" rel="noopener noreferrer"&gt;Try it with your own data&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Workflow
&lt;/h2&gt;

&lt;p&gt;I keep the &lt;a href="https://jsonlint.echovalue.dev/" rel="noopener noreferrer"&gt;linter&lt;/a&gt; bookmarked. API returns garbage, paste, fix, lint. Need to dig into the response, switch to the jq tab. It handles maybe 80% of my "what's in this JSON?" moments without opening a terminal.&lt;/p&gt;

&lt;p&gt;The whole thing is &lt;a href="https://github.com/njoylab/json-linter-tool" rel="noopener noreferrer"&gt;open source on GitHub&lt;/a&gt; if you want to look at the code or contribute.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;Part of &lt;a href="https://www.echovalue.dev" rel="noopener noreferrer"&gt;echoValue&lt;/a&gt;. Free, no signups, no tracking.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>json</category>
      <category>jq</category>
      <category>javascript</category>
      <category>webdev</category>
    </item>
    <item>
      <title>InstaTrack: Track Your Instagram Followers &amp; Following (Privacy-First, Open Source)</title>
      <dc:creator>Emiliano Saurin</dc:creator>
      <pubDate>Mon, 06 Oct 2025 13:29:12 +0000</pubDate>
      <link>https://forem.com/njoylab/instatrack-track-your-instagram-followers-following-privacy-first-open-source-5291</link>
      <guid>https://forem.com/njoylab/instatrack-track-your-instagram-followers-following-privacy-first-open-source-5291</guid>
      <description>&lt;p&gt;After getting frustrated with Instagram analytics apps that require account access or send data to their servers or are not free I built InstaTrack: a fully client-side tool to analyze followers/following.&lt;/p&gt;

&lt;p&gt;What it does:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Upload Instagram JSON exports (followers/following)&lt;/li&gt;
&lt;li&gt;See who doesn't follow you back and vice versa&lt;/li&gt;
&lt;li&gt;Track trends over time with multiple snapshots&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;All processing happens in your browser (localStorage), zero server involvement&lt;br&gt;
Try it: &lt;a href="https://instatrack.njoylab.com" rel="noopener noreferrer"&gt;https://instatrack.njoylab.com&lt;/a&gt;&lt;br&gt;
GitHub: &lt;a href="https://github.com/njoylab/instatrack" rel="noopener noreferrer"&gt;https://github.com/njoylab/instatrack&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I have few followers, so haven't tested performance with large datasets (10k+ followers). If anyone tries it with a big following, would love feedback on how it handles.&lt;/p&gt;

&lt;p&gt;Open to any feedback on UX or features!&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>programming</category>
      <category>privacy</category>
      <category>nextjs</category>
    </item>
    <item>
      <title>[iOS] Built an offline vault app - need honest UI/UX feedback</title>
      <dc:creator>Emiliano Saurin</dc:creator>
      <pubDate>Fri, 15 Aug 2025 10:07:23 +0000</pubDate>
      <link>https://forem.com/njoylab/ios-built-an-offline-vault-app-need-honest-uiux-feedback-1cph</link>
      <guid>https://forem.com/njoylab/ios-built-an-offline-vault-app-need-honest-uiux-feedback-1cph</guid>
      <description>&lt;p&gt;Hey folks,&lt;/p&gt;

&lt;p&gt;I recently shipped “In My Pocket”&lt;/p&gt;

&lt;p&gt;&lt;a href="https://apps.apple.com/us/app/in-my-pocket-offline-vault/id6444294006" rel="noopener noreferrer"&gt;https://apps.apple.com/us/app/in-my-pocket-offline-vault/id6444294006&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Main idea:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;100% offline storage, no cloud, no accounts, just local data&lt;/li&gt;
&lt;li&gt;Store key/value pairs like IBANs, loyalty cards, access codes, etc.&lt;/li&gt;
&lt;li&gt;Core features are free, a $3 one-time IAP unlocks a few extras&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Where I need help:&lt;br&gt;
I deliberately went with stock iOS components for speed and familiarity, but the result feels very plain. I’m looking for constructive UI/UX suggestions to make it cleaner and more pleasant without adding bloat.&lt;/p&gt;

&lt;p&gt;Next update: import/export between devices&lt;/p&gt;

&lt;p&gt;Would love if you could check it out and tell me what you’d change. Brutal honesty welcome.&lt;/p&gt;

</description>
      <category>ios</category>
      <category>sideprojects</category>
      <category>appstore</category>
      <category>privacy</category>
    </item>
  </channel>
</rss>
