[{"data":1,"prerenderedAt":39},["ShallowReactive",2],{"4529721":3},{"type_of":4,"id":5,"title":6,"description":7,"readable_publish_date":8,"slug":9,"path":10,"url":11,"comments_count":12,"public_reactions_count":12,"collection_id":13,"published_timestamp":14,"language":15,"subforem_id":16,"ai_disclosure_level":17,"ai_disclosure_label":18,"positive_reactions_count":12,"cover_image":13,"social_image":19,"canonical_url":20,"created_at":21,"edited_at":13,"crossposted_at":13,"published_at":14,"last_comment_at":14,"reading_time_minutes":22,"tag_list":23,"tags":24,"body_html":29,"body_markdown":30,"user":31},"article",4529721,"Auto-layout you don't have to build","Originally published on the Grafloria engineering blog.   There's a special kind of feature request:...","Aug 30","auto-layout-you-dont-have-to-build-1311","/ramy_othman/auto-layout-you-dont-have-to-build-1311","https://dev.to/ramy_othman/auto-layout-you-dont-have-to-build-1311",0,null,"2026-08-30T16:12:34Z","en",1,"not_disclosed","Not Disclosed","https://media2.dev.to/dynamic/image/width=1200,height=627,fit=cover,gravity=auto,format=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fj8y2hlum66ecpvtaqxdz.png","https://grafloria.com/blog/auto-layout-you-dont-have-to-build/","2026-08-30T16:04:19Z",2,"react, angular, vue, javascript",[25,26,27,28],"react","angular","vue","javascript","\u003Cblockquote>\n\u003Cp>Originally published on the \u003Ca href=\"https://grafloria.com/blog/auto-layout-you-dont-have-to-build/\" target=\"_blank\" rel=\"noopener noreferrer\">Grafloria engineering blog\u003C/a>.\u003C/p>\n\u003C/blockquote>\n\n\u003Cp>There's a special kind of feature request: the one filed as \u003Ca href=\"https://github.com/xyflow/xyflow/issues/5\" target=\"_blank\" rel=\"noopener noreferrer\">issue #5\u003C/a> of a project and still generating tutorials years later. For flow libraries that's auto-layout. The mainstream answer became \"integrate dagre or elk yourself\", and an entire genre of blog posts exists to walk you through the wiring — graph conversion in, position mapping out, edge cases yours forever. (The sequel, \u003Ca href=\"https://github.com/xyflow/xyflow/issues/1113\" target=\"_blank\" rel=\"noopener noreferrer\">\"is dynamic auto layouting possible?\"\u003C/a>, has 28 comments of people discovering the hard parts.)\u003C/p>\n\n\u003Ch2>\n  \u003Ca name=\"what-the-wiring-tutorials-dont-tell-you\" href=\"#what-the-wiring-tutorials-dont-tell-you\">\n  \u003C/a>\n  What the wiring tutorials don't tell you\n\u003C/h2>\n\n\u003Cp>Getting dagre to emit positions is the easy afternoon. The parts that eat the week:\u003C/p>\n\n\u003Cul>\n\u003Cli>\u003Cp>\u003Cstrong>Which algorithm, when?\u003C/strong> Layered algorithms are right for DAGs and wrong\u003Cbr>\nfor hairballs; trees want tree layouts; a picker is part of the feature.\u003C/p>\u003C/li>\n\u003Cli>\u003Cp>\u003Cstrong>ELK is 1.4 MB.\u003C/strong> The best layered engine is also the heaviest — ship it\u003Cbr>\neagerly and your bundle audit will find it.\u003C/p>\u003C/li>\n\u003Cli>\u003Cp>\u003Cstrong>The second layout.\u003C/strong> Insert a node into a laid-out graph and a full re-run\u003Cbr>\nscrambles every position the user has memorized. You need \u003Cem>incremental\u003C/em> layout, and no wiring tutorial covers it.\u003C/p>\u003C/li>\n\u003Cli>\u003Cp>\u003Cstrong>Don't fight the hands.\u003C/strong> If layout re-runs on every data change, it undoes\u003Cbr>\nthe drag your user just made. When it runs is as important as what it computes.\u003C/p>\u003C/li>\n\u003C/ul>\n\n\u003Ch2>\n  \u003Ca name=\"one-call-and-the-four-problems-it-absorbs\" href=\"#one-call-and-the-four-problems-it-absorbs\">\n  \u003C/a>\n  One call, and the four problems it absorbs\n\u003C/h2>\n\n\n\n\u003Cdiv class=\"highlight js-code-highlight\">\n\u003Cpre class=\"highlight javascript\">\u003Ccode>\u003Cspan class=\"k\">await\u003C/span> \u003Cspan class=\"nx\">engine\u003C/span>\u003Cspan class=\"p\">.\u003C/span>\u003Cspan class=\"nf\">layout\u003C/span>\u003Cspan class=\"p\">(\u003C/span>\u003Cspan class=\"dl\">'\u003C/span>\u003Cspan class=\"s1\">elk\u003C/span>\u003Cspan class=\"dl\">'\u003C/span>\u003Cspan class=\"p\">);\u003C/span>                                    \u003Cspan class=\"c1\">// picker: 'auto' inspects the graph\u003C/span>\n\u003Cspan class=\"k\">await\u003C/span> \u003Cspan class=\"nx\">engine\u003C/span>\u003Cspan class=\"p\">.\u003C/span>\u003Cspan class=\"nf\">layout\u003C/span>\u003Cspan class=\"p\">(\u003C/span>\u003Cspan class=\"dl\">'\u003C/span>\u003Cspan class=\"s1\">dagre\u003C/span>\u003Cspan class=\"dl\">'\u003C/span>\u003Cspan class=\"p\">,\u003C/span> \u003Cspan class=\"p\">{\u003C/span> \u003Cspan class=\"na\">direction\u003C/span>\u003Cspan class=\"p\">:\u003C/span> \u003Cspan class=\"dl\">'\u003C/span>\u003Cspan class=\"s1\">TB\u003C/span>\u003Cspan class=\"dl\">'\u003C/span>\u003Cspan class=\"p\">,\u003C/span> \u003Cspan class=\"na\">rankSpacing\u003C/span>\u003Cspan class=\"p\">:\u003C/span> \u003Cspan class=\"mi\">80\u003C/span> \u003Cspan class=\"p\">});\u003C/span>\n\u003Cspan class=\"k\">await\u003C/span> \u003Cspan class=\"nx\">engine\u003C/span>\u003Cspan class=\"p\">.\u003C/span>\u003Cspan class=\"nf\">layoutIncremental\u003C/span>\u003Cspan class=\"p\">({\u003C/span> \u003Cspan class=\"na\">changed\u003C/span>\u003Cspan class=\"p\">:\u003C/span> \u003Cspan class=\"p\">[\u003C/span>\u003Cspan class=\"dl\">'\u003C/span>\u003Cspan class=\"s1\">inserted-id\u003C/span>\u003Cspan class=\"dl\">'\u003C/span>\u003Cspan class=\"p\">],\u003C/span> \u003Cspan class=\"na\">radius\u003C/span>\u003Cspan class=\"p\">:\u003C/span> \u003Cspan class=\"mi\">1\u003C/span> \u003Cspan class=\"p\">});\u003C/span>  \u003Cspan class=\"c1\">// the second layout\u003C/span>\n\u003C/code>\u003C/pre>\n\u003Cdiv class=\"highlight__panel js-actions-panel\">\n\u003Cdiv class=\"highlight__panel-action js-fullscreen-code-action\">\n    \u003Csvg xmlns=\"http://www.w3.org/2000/svg\" width=\"20px\" height=\"20px\" viewbox=\"0 0 24 24\" class=\"highlight-action crayons-icon highlight-action--fullscreen-on\">\u003Ctitle>Enter fullscreen mode\u003C/title>\n    \u003Cpath d=\"M16 3h6v6h-2V5h-4V3zM2 3h6v2H4v4H2V3zm18 16v-4h2v6h-6v-2h4zM4 19h4v2H2v-6h2v4z\">\u003C/path>\n\u003C/svg>\n\n    \u003Csvg xmlns=\"http://www.w3.org/2000/svg\" width=\"20px\" height=\"20px\" viewbox=\"0 0 24 24\" class=\"highlight-action crayons-icon highlight-action--fullscreen-off\">\u003Ctitle>Exit fullscreen mode\u003C/title>\n    \u003Cpath d=\"M18 7h4v2h-6V3h2v4zM8 9H2V7h4V3h2v6zm10 8v4h-2v-6h6v2h-4zM8 15v6H6v-4H2v-2h6z\">\u003C/path>\n\u003C/svg>\n\n\u003C/div>\n\u003C/div>\n\u003C/div>\n\n\n\n\u003Cp>Registered names: \u003Ccode>auto\u003C/code>, \u003Ccode>elk\u003C/code>, \u003Ccode>dagre\u003C/code>,\u003Cbr>\n\u003Ccode>layered\u003C/code>, \u003Ccode>tree\u003C/code>, \u003Ccode>grid\u003C/code>, \u003Ccode>circular\u003C/code>,\u003Cbr>\n\u003Ccode>radial\u003C/code>, \u003Ccode>force\u003C/code>, \u003Ccode>spectral\u003C/code>, \u003Ccode>community\u003C/code>.\u003Cbr>\nELK loads as a lazy chunk on first use — it costs your bundle nothing until someone actually invokes it — and runs off-thread in a Worker. And in every framework binding, the declarative form re-runs on \u003Cem>prop-value\u003C/em> change only, never on node data, so it structurally cannot fight a user's drag:\u003Cbr>\n\u003C/p>\n\n\u003Cdiv class=\"highlight js-code-highlight\">\n\u003Cpre class=\"highlight javascript\">\u003Ccode>\u003Cspan class=\"c1\">// React\u003C/span>\n\n\u003Cspan class=\"c1\">// Angular\u003C/span>\n\n\u003Cspan class=\"c1\">// Vue\u003C/span>\n\n\u003C/code>\u003C/pre>\n\u003Cdiv class=\"highlight__panel js-actions-panel\">\n\u003Cdiv class=\"highlight__panel-action js-fullscreen-code-action\">\n    \u003Csvg xmlns=\"http://www.w3.org/2000/svg\" width=\"20px\" height=\"20px\" viewbox=\"0 0 24 24\" class=\"highlight-action crayons-icon highlight-action--fullscreen-on\">\u003Ctitle>Enter fullscreen mode\u003C/title>\n    \u003Cpath d=\"M16 3h6v6h-2V5h-4V3zM2 3h6v2H4v4H2V3zm18 16v-4h2v6h-6v-2h4zM4 19h4v2H2v-6h2v4z\">\u003C/path>\n\u003C/svg>\n\n    \u003Csvg xmlns=\"http://www.w3.org/2000/svg\" width=\"20px\" height=\"20px\" viewbox=\"0 0 24 24\" class=\"highlight-action crayons-icon highlight-action--fullscreen-off\">\u003Ctitle>Exit fullscreen mode\u003C/title>\n    \u003Cpath d=\"M18 7h4v2h-6V3h2v4zM8 9H2V7h4V3h2v6zm10 8v4h-2v-6h6v2h-4zM8 15v6H6v-4H2v-2h6z\">\u003C/path>\n\u003C/svg>\n\n\u003C/div>\n\u003C/div>\n\u003C/div>\n\n\n\n\u003Cp>See every algorithm on a real graph: \u003Ca href=\"https://grafloria.com/demos/#layout\" target=\"_blank\" rel=\"noopener noreferrer\">layout demos\u003C/a> · the concept guide (including the choosing table): \u003Ca href=\"https://grafloria.com/learn/layout/\" target=\"_blank\" rel=\"noopener noreferrer\">Layout\u003C/a>.\u003C/p>\n\n\n\u003Chr>\n\n\u003Cp>\u003Cem>Grafloria is an MIT-licensed diagram engine for React, Angular, Vue and plain JavaScript — \u003Ca href=\"https://grafloria.com/\" target=\"_blank\" rel=\"noopener noreferrer\">grafloria.com\u003C/a>. If this post was useful, the \u003Ca href=\"https://grafloria.com/demos/\" target=\"_blank\" rel=\"noopener noreferrer\">demo gallery\u003C/a> is where the ideas live as running code.\u003C/em>\u003C/p>\n\n","> Originally published on the [Grafloria engineering blog](https://grafloria.com/blog/auto-layout-you-dont-have-to-build/).\n\nThere's a special kind of feature request: the one filed as [issue #5](https://github.com/xyflow/xyflow/issues/5) of a project and still generating tutorials years later. For flow libraries that's auto-layout. The mainstream answer became \"integrate dagre or elk yourself\", and an entire genre of blog posts exists to walk you through the wiring — graph conversion in, position mapping out, edge cases yours forever. (The sequel, [\"is dynamic auto layouting possible?\"](https://github.com/xyflow/xyflow/issues/1113), has 28 comments of people discovering the hard parts.)\n\n## What the wiring tutorials don't tell you\n\nGetting dagre to emit positions is the easy afternoon. The parts that eat the week:\n\n- **Which algorithm, when?** Layered algorithms are right for DAGs and wrong\nfor hairballs; trees want tree layouts; a picker is part of the feature.\n\n- **ELK is 1.4 MB.** The best layered engine is also the heaviest — ship it\neagerly and your bundle audit will find it.\n\n- **The second layout.** Insert a node into a laid-out graph and a full re-run\nscrambles every position the user has memorized. You need *incremental* layout, and no wiring tutorial covers it.\n\n- **Don't fight the hands.** If layout re-runs on every data change, it undoes\nthe drag your user just made. When it runs is as important as what it computes.\n\n## One call, and the four problems it absorbs\n\n```javascript\nawait engine.layout('elk');                                    // picker: 'auto' inspects the graph\nawait engine.layout('dagre', { direction: 'TB', rankSpacing: 80 });\nawait engine.layoutIncremental({ changed: ['inserted-id'], radius: 1 });  // the second layout\n```\n\nRegistered names: `auto`, `elk`, `dagre`,\n`layered`, `tree`, `grid`, `circular`,\n`radial`, `force`, `spectral`, `community`.\nELK loads as a lazy chunk on first use — it costs your bundle nothing until someone actually invokes it — and runs off-thread in a Worker. And in every framework binding, the declarative form re-runs on *prop-value* change only, never on node data, so it structurally cannot fight a user's drag:\n\n```javascript\n// React\n\n// Angular\n\n// Vue\n\n```\n\nSee every algorithm on a real graph: [layout demos](https://grafloria.com/demos/#layout) · the concept guide (including the choosing table): [Layout](https://grafloria.com/learn/layout/).\n\n---\n\n*Grafloria is an MIT-licensed diagram engine for React, Angular, Vue and plain JavaScript — [grafloria.com](https://grafloria.com/). If this post was useful, the [demo gallery](https://grafloria.com/demos/) is where the ideas live as running code.*",{"name":32,"username":33,"twitter_username":13,"github_username":34,"user_id":35,"website_url":36,"profile_image":37,"profile_image_90":38},"Ramy Othman","ramy_othman","visionarerthm",4101566,"https://grafloria.com","https://media2.dev.to/dynamic/image/width=640,height=640,fit=cover,gravity=auto,format=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F4101566%2F943518a4-ccb8-4ccb-bad0-f86d168e24f8.png","https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F4101566%2F943518a4-ccb8-4ccb-bad0-f86d168e24f8.png",1788921586614]