I wanted one clean place for the small Instagram chores people google every day: fancy bio fonts, keeping line breaks in captions, splitting a photo into a profile grid, posting without cropping, and watching public stories without logging in.
So I built IGKit. The constraint I set myself: no server cost until it earns money. Here is how that worked out.
The stack (or lack of it)
- A ~600 line
build.mjsthat renders every page from plain template strings intodist/. No React, no bundler, no dependencies. - Vanilla JS per tool, loaded with
defer. - Two edge functions for the one feature that needs a server.
- Hosting on a free tier, analytics on the free tier.
The whole site is 47 pages: 5 tools, 14 long-tail font pages, 4 viewer landing pages and the two most popular tools translated into 10 languages.
Fonts are just Unicode math letters
Instagram has no font picker, but it renders any Unicode character. "Bold" is U+1D400, "script" is U+1D49C, and so on. The only catch is the gaps: some script and fraktur letters were added to Unicode earlier, so they live in the Letterlike Symbols block instead.
function range(upper, lower, digits, exceptions = {}) {
return (ch) => {
if (exceptions[ch]) return exceptions[ch];
const c = ch.codePointAt(0);
if (upper != null && c >= 65 && c <= 90) return String.fromCodePoint(upper + c - 65);
if (lower != null && c >= 97 && c <= 122) return String.fromCodePoint(lower + c - 97);
if (digits != null && c >= 48 && c <= 57) return String.fromCodePoint(digits + c - 48);
return ch;
};
}
const script = range(0x1d49c, 0x1d4b6, null, {
B: 'ℬ', E: 'ℰ', F: 'ℱ', H: 'ℋ', I: 'ℐ', L: 'ℒ', M: 'ℳ', R: 'ℛ', e: 'ℯ', g: 'ℊ', o: 'ℴ',
});
Without that exceptions map you get empty boxes for exactly those letters, which is the most common bug in font generators I tested.
Keeping blank lines in captions
Instagram trims empty lines and leading spaces. The fix is the Braille blank U+2800: it counts as a real character, so it survives, but it renders as nothing.
const BLANK = '⠀';
const format = (text) =>
text.split('\n').map((line) => {
const trimmed = line.replace(/\s+$/, '');
return trimmed === '' ? BLANK : trimmed.replace(/^ +/, (s) => BLANK.repeat(s.length));
}).join('\n');
Grids changed to 3:4
Instagram's profile grid now shows 3:4 thumbnails, so the old "cut into squares" grid makers produce misaligned profiles. The grid tool center-crops to the whole grid's ratio, slices 1080x1440 tiles on a canvas, and numbers them in posting order (bottom-right first, because the newest post lands top-left).
The one feature that needs a server
Story viewing needs data from Instagram. I don't scrape it myself; I call third-party data APIs through an edge function, and I treat their free tiers as the budget:
- Rotate providers. Each free plan is tiny (20 to 100 calls a month) and some enforce their own stricter limits. The function tries providers in order and falls through on a 403/429.
-
Cache at the CDN.
cache-control: public, s-maxage=3600, stale-while-revalidate=10800. Popular accounts get looked up by many people, so one upstream call serves everyone for an hour. -
Proxy media through an allowlist. Instagram CDN URLs don't always load cross-origin, so images and videos stream through a second edge function that only accepts
*.cdninstagram.comand*.fbcdn.net, which keeps it from becoming an open proxy. - Degrade gracefully. When every quota is used up, the page says so and points to the other tools instead of showing an error.
SEO for a brand-new domain
A new site will not rank for "instagram fonts" next month, so I went wide instead of deep:
- One page per specific intent (cursive, bold, small caps, gothic, TikTok, Discord...) with its own copy and FAQ schema.
- Translations for big Instagram markets (es, pt, id, hi, fr, de, tr, ru, ja, ar with RTL) linked with
hreflang. - IndexNow for Bing and friends on every deploy, plus a sitemap for Google.
What I'd love feedback on
- Which tool is missing? Highlights viewer and reels downloader are next on my list.
- Anyone else run multi-provider fallbacks for free API tiers? Curious how you handle quota tracking.
The site: https://igkit-tools.vercel.app
Top comments (0)