<?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: erdiagram</title>
    <description>The latest articles on DEV Community by erdiagram (@erdiagram).</description>
    <link>https://dev.to/erdiagram</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%2F1804219%2F45b714e0-e993-45c5-9e97-87fe20027f6e.png</url>
      <title>DEV Community: erdiagram</title>
      <link>https://dev.to/erdiagram</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/erdiagram"/>
    <language>en</language>
    <item>
      <title>No signup, just boxes and arrows: building a free ER diagram editor</title>
      <dc:creator>erdiagram</dc:creator>
      <pubDate>Thu, 04 Jun 2026 06:37:43 +0000</pubDate>
      <link>https://dev.to/erdiagram/no-signup-just-boxes-and-arrows-building-a-free-er-diagram-editor-36bk</link>
      <guid>https://dev.to/erdiagram/no-signup-just-boxes-and-arrows-building-a-free-er-diagram-editor-36bk</guid>
      <description>&lt;p&gt;I just wanted to sketch a database schema. Five tables, a couple of foreign keys, nothing fancy.&lt;/p&gt;

&lt;p&gt;I opened the usual suspects, and every single one met me with the same wall: create an account, confirm your email, maybe pick a plan, &lt;em&gt;then&lt;/em&gt; you can draw a box. For a thirty-second sketch.&lt;/p&gt;

&lt;p&gt;So I did the most predictable developer thing imaginable and built my own. It's live at &lt;a href="https://erdiagram.dev/?utm_source=devto" rel="noopener noreferrer"&gt;erdiagram.dev&lt;/a&gt; — free, and you can start drawing the moment the page loads. No signup, no modal, nothing in the way.&lt;/p&gt;

&lt;p&gt;This is the &lt;em&gt;why&lt;/em&gt;, plus the three technical decisions I'd actually defend in a code review:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;The browser is the backend for guests (so there's no signup wall).&lt;/li&gt;
&lt;li&gt;Canvas instead of DOM/SVG for the renderer.&lt;/li&gt;
&lt;li&gt;A boring last-write-wins sync protocol instead of CRDTs.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Let's get into it.&lt;/p&gt;

&lt;h2&gt;
  
  
  1. The one rule: it has to work before you have an account
&lt;/h2&gt;

&lt;p&gt;The whole project hangs on a single principle: &lt;strong&gt;the editor works before you sign up.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;That sounds trivial, but it shaped a surprising amount of the architecture. A guest needs to draw tables, drag them, connect foreign keys, undo/redo, import SQL — and have all of it survive a page reload. With no user row to hang anything on.&lt;/p&gt;

&lt;p&gt;The fix was to treat the browser itself as the backend for guests. Each visitor gets a &lt;code&gt;Guest_ID&lt;/code&gt; (a UUID v4 in &lt;code&gt;localStorage&lt;/code&gt;) and their whole diagram lives in &lt;strong&gt;IndexedDB&lt;/strong&gt;. No network round-trip, no server record, no "your trial expires in 7 days." The data is theirs, on their machine, for as long as they want it.&lt;/p&gt;

&lt;p&gt;If they later decide to sign up, the local diagram migrates to their account in one step. The guest mode isn't a crippled demo — it's the real editor with local persistence instead of cloud sync. Only the genuinely account-dependent features sit behind login (share links, version snapshots, larger table limits).&lt;/p&gt;

&lt;p&gt;If you're building a tool, I'd push this hard: &lt;strong&gt;"let me just try it" is the most fragile moment in your funnel.&lt;/strong&gt; Don't spend it on a registration form.&lt;/p&gt;

&lt;h2&gt;
  
  
  2. Why Canvas instead of SVG or DOM nodes
&lt;/h2&gt;

&lt;p&gt;The first real fork in the road was the renderer.&lt;/p&gt;

&lt;p&gt;The web-native instinct is to make each table a &lt;code&gt;&amp;lt;div&amp;gt;&lt;/code&gt; (or an SVG &lt;code&gt;&amp;lt;g&amp;gt;&lt;/code&gt;) and let the browser lay it out. It's pleasant to write, you get CSS for free, and it works great — right up until someone pastes in a schema with 80 tables and a few hundred relationship lines. Now the browser is reflowing and repainting a massive DOM tree on every drag frame, and the whole thing turns to syrup.&lt;/p&gt;

&lt;p&gt;So I went with &lt;strong&gt;&lt;a href="https://www.leaferjs.com/" rel="noopener noreferrer"&gt;LeaferJS&lt;/a&gt;, a Canvas 2D engine&lt;/strong&gt;. Tables, columns, relationship lines, anchors, the grid background — all painted to one canvas. The tradeoff is real, and worth being honest about:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;You lose&lt;/strong&gt; the free stuff: no CSS, no DOM accessibility tree, no "just add a &lt;code&gt;:hover&lt;/code&gt; in a stylesheet." You re-implement hit-testing, text layout, and z-ordering yourself.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;You gain&lt;/strong&gt; a flat render surface that doesn't care whether there are 5 shapes or 5,000. Pan and zoom are a transform on one node. Dragging moves objects in a scene graph instead of thrashing layout.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;For a diagramming tool — many objects, constant spatial manipulation, smooth pan/zoom as table stakes — Canvas was right. For a form-heavy app it would've been exactly wrong. &lt;strong&gt;Pick the renderer that matches the shape of the interaction, not the one that's in fashion.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;That core editor ended up isolated enough to be its own module: parsers (SQL DDL and DBML, both directions), layout algorithms (tree / force / grid), PNG and SVG exporters, and the canvas engine — all decoupled from the SvelteKit app around it.&lt;/p&gt;

&lt;h2&gt;
  
  
  3. The stack, boring on purpose
&lt;/h2&gt;

&lt;p&gt;The app shell is &lt;strong&gt;SvelteKit (Svelte 5)&lt;/strong&gt;. Svelte 5's runes (&lt;code&gt;$state&lt;/code&gt;, &lt;code&gt;$derived&lt;/code&gt;, &lt;code&gt;$effect&lt;/code&gt;) made the reactive glue between the canvas and the surrounding UI — toolbar, side panel, the save-status dot — easy to reason about without dragging in a state-management library.&lt;/p&gt;

&lt;p&gt;Everything else is deliberately unexciting, which I count as a feature:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;SQLite via &lt;code&gt;better-sqlite3&lt;/code&gt;&lt;/strong&gt; — synchronous, embedded, no separate DB process. For a single-region app it's plenty, and it makes deploys trivial.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;A custom Node server&lt;/strong&gt; serving both HTTP and WebSocket from one process.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;JWT in an HttpOnly cookie&lt;/strong&gt; for auth, &lt;strong&gt;bcrypt&lt;/strong&gt; for passwords, email codes via Resend.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Fly.io&lt;/strong&gt;, single region, one SQLite volume. The whole monthly bill is single digits.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I call out the cost because "indie tool that needs $200/month of infra to break even" is a trap. Boring, cheap, boring again.&lt;/p&gt;

&lt;h2&gt;
  
  
  4. The part I rewrote: real-time sync
&lt;/h2&gt;

&lt;p&gt;For signed-in users, diagrams live in the cloud and sync over WebSocket with autosave. This is the piece I overthought first, then deliberately &lt;em&gt;under&lt;/em&gt;-thought into something that actually ships.&lt;/p&gt;

&lt;p&gt;No operational transforms, no CRDTs. I'm one person, and the realistic concurrency here is "same user, two tabs" or "two people glancing at a diagram together" — not Google-Docs-scale co-editing. So the protocol is intentionally dumb:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The &lt;strong&gt;server is the single authority&lt;/strong&gt; for a monotonic revision number (&lt;code&gt;rev&lt;/code&gt;).&lt;/li&gt;
&lt;li&gt;A client commits a full snapshot tagged with the &lt;code&gt;baseRev&lt;/code&gt; it started from.&lt;/li&gt;
&lt;li&gt;If &lt;code&gt;baseRev === serverRev&lt;/code&gt;, the server accepts, bumps &lt;code&gt;rev&lt;/code&gt;, and broadcasts to peers.&lt;/li&gt;
&lt;li&gt;If &lt;code&gt;baseRev &amp;lt; serverRev&lt;/code&gt; (someone committed in between), the server rejects with a &lt;code&gt;conflict&lt;/code&gt; and the current state. The client adopts the new &lt;code&gt;rev&lt;/code&gt; and &lt;strong&gt;re-pushes its own snapshot&lt;/strong&gt; — last write wins, with an automatic rebase.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;There's exactly one in-flight commit at a time; edits that pile up during the round-trip coalesce and go out on the next tick. And since a browser can die mid-commit, the pending snapshot is also buffered in IndexedDB — a crash before the ack doesn't lose the edit.&lt;/p&gt;

&lt;p&gt;Is last-write-wins "correct" in the academic sense? No. But it's &lt;em&gt;predictable&lt;/em&gt;, it's a few hundred lines instead of a few thousand, and it fits how the tool is actually used. &lt;strong&gt;Picking the simplest model that matches your real concurrency — instead of the most impressive one — was one of my better calls.&lt;/strong&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  The two features I almost cut
&lt;/h2&gt;

&lt;p&gt;Both turned out to matter more than I expected:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Internationalization.&lt;/strong&gt; I added 10 languages (English, Chinese, Japanese, Korean, Portuguese, Spanish, German, French, Russian, Indonesian) almost on a whim. Developer-tool markets in non-English languages are far less crowded, and once the i18n plumbing exists, the marginal cost is just translation strings.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Import/export that round-trips.&lt;/strong&gt; SQL DDL in, diagram out; diagram out, SQL or DBML back. The editor isn't a box you get stuck inside — paste your existing schema, get a picture, tweak it, take the SQL with you. Tools that hold your data hostage feel hostile, and developers notice.&lt;/p&gt;

&lt;h2&gt;
  
  
  What's next
&lt;/h2&gt;

&lt;p&gt;There's a &lt;a href="https://erdiagram.dev/templates?utm_source=devto" rel="noopener noreferrer"&gt;&lt;code&gt;/templates&lt;/code&gt;&lt;/a&gt; section now — ready-made schemas for e-commerce, blogs, SaaS, hospitals, schools, booking systems — each one a click away from opening in the editor. On the roadmap: PDF export, virtualized rendering for genuinely huge diagrams, and React/Vue wrappers for the core library.&lt;/p&gt;

&lt;p&gt;But the thing I'm still proudest of is the boring one: you land on the page and you start drawing. No account. No friction. Just boxes and arrows, the way it should be.&lt;/p&gt;

&lt;p&gt;Kick the tires at &lt;strong&gt;&lt;a href="https://erdiagram.dev/?utm_source=devto" rel="noopener noreferrer"&gt;erdiagram.dev&lt;/a&gt;&lt;/strong&gt; — and I'd genuinely love to hear what breaks. Drop a comment, especially if you throw a gnarly schema at it and the auto-layout does something ridiculous.&lt;/p&gt;

&lt;p&gt;Thanks for reading. Back to the canvas.&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>svelte</category>
      <category>sql</category>
      <category>sideprojects</category>
    </item>
  </channel>
</rss>
