<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>Emil Ahlbäck</title>
    <link>https://emil.zip/blog</link>
    <description>Writing by Emil Ahlbäck about software engineering, AI, Lovable, and building products.</description>
    <language>en</language>
    <lastBuildDate>Tue, 10 Feb 2026 14:21:25 GMT</lastBuildDate>
    <atom:link href="https://emil.zip/rss.xml" rel="self" type="application/rss+xml" />
    <item>
      <title>How I Built Lovable&apos;s Enterprise Offering from 0 to 1</title>
      <link>https://emil.zip/blog/enterprise-bet</link>
      <guid isPermaLink="true">https://emil.zip/blog/enterprise-bet</guid>
      <description>As Lovable&apos;s founding engineer, I built its enterprise offering from 0 to 1 and helped make it the company&apos;s fastest-growing segment.</description>
      <content:encoded>&lt;p&gt;As a founding engineer at Lovable, I built the company&amp;#x27;s enterprise offering from zero. I owned the initial architecture and implementation, shipped workspaces, multi-tenancy, SSO, SOC 2, private deployments, and configurable toolchains, and personally onboarded our first twenty enterprise customers. It became Lovable&amp;#x27;s fastest-growing segment.&lt;/p&gt;
&lt;p&gt;Last year, I joined a customer call expecting to help a solo founder debug a deployment issue. Instead, I found myself talking to a product lead at a Fortune 500 company. Her entire team was already using Lovable daily. They&amp;#x27;d built internal tools, prototyped features, shipped experiments. She just wanted to know how to pay for it properly.&lt;/p&gt;
&lt;p&gt;This kept happening. Call after call, the same pattern: people at large companies who&amp;#x27;d discovered Lovable on their own, built something real, and now wanted to bring it to their teams officially. Enterprise demand was already there, hiding in our user base.&lt;/p&gt;
&lt;h2&gt;The Opportunity&lt;/h2&gt;
&lt;p&gt;To me, this looked like a market. To others, it looked like a distraction. Within a year, Jensen Huang would name Lovable alongside Cursor and OpenAI as part of the future enterprise workforce, and NVIDIA would back our Series B. But at the time, the idea was controversial.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Enterprise has killed companies before.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;The pushback came from people who&amp;#x27;d seen it happen. Senior engineers who&amp;#x27;d watched codebases become unmaintainable after enterprise deals.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;This will split our focus. We&amp;#x27;ll end up with two half-baked products instead of one good one.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;They weren&amp;#x27;t wrong to worry. Lovable was growing fast on the consumer side. Chasing enterprise could pull resources away from what was working. It could bloat the codebase. It could turn us into the kind of company we&amp;#x27;d never wanted to be.&lt;/p&gt;
&lt;p&gt;I took these seriously. They&amp;#x27;d seen what happens when you don&amp;#x27;t. The question became: how do we pursue this without becoming another cautionary tale?&lt;/p&gt;
&lt;h2&gt;The Constraint&lt;/h2&gt;
&lt;p&gt;I landed on a rule: only build features that benefit everyone, or don&amp;#x27;t build them at all.&lt;/p&gt;
&lt;p&gt;If a feature couldn&amp;#x27;t be a natural extension of the core product, we&amp;#x27;d find a different approach or say no. No enterprise-specific sprawl. No forking the codebase. One product serving everyone.&lt;/p&gt;
&lt;p&gt;This came directly from taking the concerns seriously. And it turned out to be clarifying. Every decision had a simple test: does this make the product better for a solo founder too?&lt;/p&gt;
&lt;p&gt;Workspaces let solo founders separate client projects. SSO removed password friction for teams of any size. Improved security helped us gain more trust. Each &amp;quot;enterprise&amp;quot; feature found a second life as a product improvement.&lt;/p&gt;
&lt;p&gt;Conventional wisdom says enterprise needs its own product. We decided to prove otherwise.&lt;/p&gt;
&lt;h2&gt;Building Lovable&amp;#x27;s Enterprise Offering&lt;/h2&gt;
&lt;p&gt;Multi-tenancy, SSO, SOC 2, private deploys, configurable toolchains. Each piece was blocking deals. The constraint made every one a design problem: how do you build this so it helps everyone?&lt;/p&gt;
&lt;p&gt;We called them workspaces, not &amp;quot;enterprise organizations,&amp;quot; because solo founders used them too. SSO removed friction for teams of any size. Security hardening protected all users, not just the ones paying enterprise prices. Each one passed the test.&lt;/p&gt;
&lt;p&gt;In spring and summer 2025, I built the entire enterprise stack myself alongside working on Lovable Cloud, visual edits, and other major features. In June, we shipped a self-serve B2B plan. Teams could just sign up and start collaborating on Lovable, no sales call required. Our enterprise operation was me handling all the engineering, and three GTMs. No cold outreach and no traditional sales motion.&lt;/p&gt;
&lt;p&gt;I personally onboarded our first twenty customers. Joined their calls, understood their blockers, shipped what they needed. This was the real education. What enterprise teams actually care about versus what I&amp;#x27;d assumed from reading about B2B. Every conversation tested the constraint. Most of what they needed fit. When it didn&amp;#x27;t, we found a way to make it fit, or said no.&lt;/p&gt;
&lt;h2&gt;The Hackathon&lt;/h2&gt;
&lt;p&gt;In the middle of this, our largest customer wanted to run a company-wide hackathon. 50,000+ employees, all collaborating on one workspace which currently only supported hundreds of members at most. We had one week.&lt;/p&gt;
&lt;p&gt;Our infrastructure wasn&amp;#x27;t built for that. We shipped the night before. It worked.&lt;/p&gt;
&lt;p&gt;That was the constraint paying off. One codebase handled tens of thousands of enterprise users the same way it handled solo founders. Because we&amp;#x27;d never forked it.&lt;/p&gt;
&lt;h2&gt;What We Learned&lt;/h2&gt;
&lt;p&gt;Enterprise became our fastest-growing segment. By the end of 2025, over half of Fortune 500 companies had teams on the platform. Microsoft, Uber, HCA Healthcare, Zendesk, ElevenLabs, all using Lovable daily. Jensen Huang named us alongside Cursor and OpenAI as the AI tools shaping future enterprise workforces.&lt;/p&gt;
&lt;p&gt;One company replaced Slack and their entire intranet with what they built in Lovable, fired their marketing agency, and hired three full-time vibe coders. They estimated they&amp;#x27;d saved millions. Another launched 26 country websites in a single week.&lt;/p&gt;
&lt;p&gt;The bet held up. One codebase, no bloat. When we raised our Series B, Salesforce Ventures, Atlassian Ventures, Databricks Ventures, HubSpot Ventures, and NVIDIA&amp;#x27;s NVentures all backed us. Enterprise software companies backing a product that refused to fork.&lt;/p&gt;
&lt;p&gt;One constraint, applied consistently, got us further than anyone expected. The same product that serves a solo founder prototyping on a weekend can serve an enterprise team shipping internal tools. That was always the point.&lt;/p&gt;</content:encoded>
      <dc:creator>Emil Ahlbäck</dc:creator>
      <pubDate>Tue, 10 Feb 2026 14:21:25 GMT</pubDate>
    </item>
    <item>
      <title>How I built an Instant, Figma-Like UX Editor</title>
      <link>https://emil.zip/blog/visual-edits</link>
      <guid isPermaLink="true">https://emil.zip/blog/visual-edits</guid>
      <description>Learn how I built the Visual Edits feature at Lovable, an intuitive, Figma-like visual editor that empowers users to visually select, edit, and save instant frontend changes directly in their apps.</description>
      <content:encoded>&lt;p&gt;In the past three weeks, I developed and launched one of the most technically challenging and exciting features I&amp;#x27;ve ever worked on at &lt;a href=&quot;https://lovable.dev&quot;&gt;Lovable&lt;/a&gt;, our AI-powered full-stack app-building platform. Lovable users can already generate a fully functional application by just prompting our AI (&lt;a href=&quot;https://x.com/emilahlback/status/1894488375517671795&quot;&gt;x.com&lt;/a&gt;). As great as Lovable already was for instantly generating apps, we realized developers and designers often want &lt;em&gt;more precise control&lt;/em&gt; over the final touches.&lt;/p&gt;
&lt;p&gt;To solve this, I set out to build what became the &lt;a href=&quot;https://x.com/lovable_dev/status/1890075209748902304&quot;&gt;Visual Edits&lt;/a&gt; feature: an intuitive, Figma-like visual editor that empowers users (regardless of frontend skill) to visually select, edit, and save instant frontend changes directly in their apps.&lt;/p&gt;
&lt;p&gt;This post explores how Visual Edits is engineered, why it was a particularly exciting technical challenge and why solving complex frontend UX problems is crucial to our mission at Lovable of transforming how apps are built.&lt;/p&gt;
&lt;h2&gt;Problem 1: Tightening the Loop from Idea to Implementation&lt;/h2&gt;
&lt;p&gt;Traditionally, UI development cycles follow a painful loop: we tweak CSS, click save, wait for rebuild, refresh browser, notice mistakes, adjust again... iteration is slow, frustrating, and error-prone. AI generation is amazing for fast kick-offs, but when you need to polish your frontend, precise iteration becomes crucial:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;You want instant visual feedback&lt;/li&gt;
&lt;li&gt;You need reliability in applying styles consistently&lt;/li&gt;
&lt;li&gt;You require clean, maintainable code with minimal AI hallucination in the final polish stage&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Problem 2: AI is still (relatively) expensive&lt;/h2&gt;
&lt;p&gt;AI costs continue to drop, but they&amp;#x27;re still expensive. Especially when it needs context on the full app even for small changes.&lt;/p&gt;
&lt;p&gt;Each regeneration costs both time and money - for us running the infrastructure and for users waiting for results. Visual Edits dramatically reduces these costs by allowing precise, targeted changes without touching the AI at all.&lt;/p&gt;
&lt;p&gt;This makes it cheaper to create great products with greater precision.&lt;/p&gt;
&lt;h2&gt;Our Approach: Bring the Editor into the Browser&lt;/h2&gt;
&lt;p&gt;We decided to move part of the code layer closer to the visualization layer in-browser, creating a WYSIWYG editor. Often WYSIWYG tools abstract away code entirely. Our approach instead keeps the direct, bi-directional connection between visual edits and the underlying source code. Here&amp;#x27;s how it works under the hood:&lt;/p&gt;
&lt;figure&gt;&lt;img src=&quot;https://emil.zip/content/news/selecting-text.png&quot; alt=&quot;Selecting elements&quot;/&gt;&lt;figcaption&gt;Selecting elements with Lovable Visual Edits&lt;/figcaption&gt;&lt;/figure&gt;
&lt;h3&gt;1. Stable JSX Tagging &amp;amp; Instant Cloud Dev Servers&lt;/h3&gt;
&lt;p&gt;When you start a Lovable project, an ephemeral dev server spins up instantly in the cloud.
How this works is worthy of a whole post in itself. Basically, we continually host 4k+ instances on &lt;a href=&quot;https://fly.io&quot;&gt;fly.io&lt;/a&gt; to serve Lovable projects,
and at compile-time each JSX component our AI generates is tagged with a unique, stable ID using our custom Vite plugin.
These stable IDs can persist across visual changes.&lt;/p&gt;
&lt;p&gt;Our infrastructure orchestrates these containers in clusters across multiple regions, with each container running an isolated Node.js environment containing a full copy of your application code. This approach allows us to scale horizontally based on demand while maintaining consistent performance regardless of project complexity or user location.&lt;/p&gt;
&lt;p&gt;When you visually select any DOM element in the app, we instantly trace it back to the exact JSX responsible for rendering it. This ensures precise bi-directional mapping:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Visual to code&lt;/strong&gt;: Clicking element → Matched to reliable JSX location&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Code to visual&lt;/strong&gt;: JSX changes → Immediate reflected changes in UI&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;2. Client-Side AST and Tailwind Generation&lt;/h3&gt;
&lt;p&gt;We sync the project&amp;#x27;s code entirely into the browser, representing it as an Abstract Syntax Tree (AST), essentially an interactive, live data structure reflecting the entire application structure.
&lt;a href=&quot;https://babeljs.io/docs/en/babel-parser&quot;&gt;Babel&lt;/a&gt; and &lt;a href=&quot;https://swc.rs&quot;&gt;SWC&lt;/a&gt; are both great libraries for this.
There&amp;#x27;s a ton of cool things we can do once we have the AST client-side. For example:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Safely make declarative changes to the source code&lt;/li&gt;
&lt;li&gt;Propagate changes to the DOM &amp;quot;optimistically&amp;quot; in real-time&lt;/li&gt;
&lt;li&gt;No need for network roundtrips when iterating&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Theoretically, one could rely on Regexes for this functionality (which is kind of similar to how AST parsers work under the hood), but having the actual AST available allows us to make far more robust and maintainable code.&lt;/p&gt;
&lt;p&gt;For example, consider updating a component&amp;#x27;s styling. Without AST parsing, we might resort to dangerous regex replacements:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-typescript&quot;&gt;// Dangerous approach without AST
function updateComponentStyles(fileContent, componentName, newStyle) {
  const regex = new RegExp(`&amp;lt;${componentName}[^&amp;gt;]*className=&amp;quot;([^&amp;quot;]*)&amp;quot;[^&amp;gt;]*&amp;gt;`, &amp;#x27;g&amp;#x27;);
  return fileContent.replace(regex, (match, className) =&amp;gt; {
    return match.replace(className, newStyle);
  });
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This approach fails with nested components, complex JSX, or when components use dynamic expressions. With AST parsing, we can safely traverse the component tree and make precise modifications:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-typescript&quot;&gt;export function updateComponentStyles(ast, componentName, newStyle) {
  traverse(ast, {
    JSXElement(path) {
      const element = path.node.openingElement;
      if (element.name.name === componentName) {
        const classAttr = element.attributes.find(
          attr =&amp;gt; attr.type === &amp;#x27;JSXAttribute&amp;#x27; &amp;amp;&amp;amp; attr.name.name === &amp;#x27;className&amp;#x27;
        );

        if (classAttr &amp;amp;&amp;amp; classAttr.value.type === &amp;#x27;StringLiteral&amp;#x27;) {
          classAttr.value.value = newStyle;
        }
      }
    }
  });
  return generate(ast).code;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;It also makes some otherwise difficult things very easy, like extracting a JSX tree of all components:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-typescript&quot;&gt;export function toJSXTree(ast: t.File) {
  const tree: TreeNode[] = [];
  const elems = new Map&amp;lt;t.Node, TreeNode&amp;gt;();

  // Traverse the AST and build a tree of JSX elements
  traverse(ast, {
    JSXElement: (path) =&amp;gt; {
      const node: TreeNode = {
        type: path.node.openingElement.name.name,
        children: [],
      };
      elems.set(path.node as t.Node, node);

      let parent: typeof path.parentPath | null = path.parentPath;
      while (parent) {
        if (parent.node &amp;amp;&amp;amp; elems.has(parent.node as t.Node)) {
          elems.get(parent.node as t.Node)?.children.push(node);
          break;
        }
        parent = parent.parentPath;
      }

      if (!parent) tree.push(node);
    },
  });

  return tree;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;When you visually tweak a component by for example adjusting Tailwind classes, these changes are optimistically applied by a client-side Tailwind generator that intelligently reads your custom configurations. Even before hitting save, your visual edits are previewed exactly as they&amp;#x27;d appear post-save, thanks to client-side AST mutations and instant Tailwind parsing.&lt;/p&gt;
&lt;figure&gt;&lt;img src=&quot;https://emil.zip/content/news/screenshot_2025-03-03_18.12.16.png&quot; alt=&quot;Example of an AST&quot;/&gt;&lt;figcaption&gt;Example of an AST&lt;/figcaption&gt;&lt;/figure&gt;
&lt;h3&gt;3. HMR&lt;/h3&gt;
&lt;p&gt;Thanks to Vite and our persistent Dev Servers, we can immediately trigger a Hot Module Replacement (HMR), refreshing the preview without ever needing an explicit page reload. Upon clicking save, a chain of events kicks off instantly:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Generate clean, standard-compliant JSX/TSX from the modified AST&lt;/li&gt;
&lt;li&gt;Compute diffs to update only precisely modified lines&lt;/li&gt;
&lt;li&gt;Push changes securely to the cloud-hosted environment&lt;/li&gt;
&lt;li&gt;Immediately trigger a hot module replacement (HMR) event back to your session, refreshing the preview without ever needing an explicit page reload&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;This round-trip, from intuitive visual interaction to production-grade code, happens seamlessly in seconds - enabling developers and designers to iterate at speeds previously impossible without losing control over code quality.&lt;/p&gt;
&lt;figure&gt;&lt;img src=&quot;https://emil.zip/content/news/screenshot-2025-03-03-at-18.03.00.png&quot; alt=&quot;Color picker&quot;/&gt;&lt;figcaption&gt;The color picker interface allows for precise visual tweaking of component styles&lt;/figcaption&gt;&lt;/figure&gt;
&lt;h2&gt;Why does this matter?&lt;/h2&gt;
&lt;p&gt;Speeding up the iteration cycle isn&amp;#x27;t just about convenience—I&amp;#x27;ve personally felt firsthand how it transforms the way we build products:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Higher quality: When making small edits becomes effortless and instant, you&amp;#x27;re far more likely to experiment, tweak, and polish your UI until it feels perfect.&lt;/li&gt;
&lt;li&gt;Reduced mental strain: Every time I don&amp;#x27;t need to mentally simulate how a bit of CSS or component state might behave, it frees me up to focus more creatively and spend less time debugging.&lt;/li&gt;
&lt;li&gt;Better communication: With visual edits becoming a core part of our development workflow, designers and engineers naturally collaborate more closely. It&amp;#x27;s incredible seeing how intuitively everyone can participate in shaping the final product.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;On the technical side, bringing Visual Edits to life pushed me to deeply integrate complex technologies like client-side AST handling, dynamic in-browser compilation, custom code generation, rapid cloud deployments, and fine-grained React component controls. If you&amp;#x27;re an engineer who loves tackling challenging, real-world problems, building this feature at Lovable has felt like the ultimate playground.&lt;/p&gt;
&lt;h2&gt;What&amp;#x27;s next&lt;/h2&gt;
&lt;p&gt;Launching Visual Edits has opened up some exciting possibilities. I&amp;#x27;m already imagining how we can build on this foundation in the near future:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Having real-time, persistent code editing experiences using a combination of AI and contextual user decisions.&lt;/li&gt;
&lt;li&gt;Allowing instant, app-wide theming.&lt;/li&gt;
&lt;li&gt;Surfacing essential tools without needing to go through external services and tools.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;I&amp;#x27;m really looking forward to seeing the impact these ideas will have on the experience of building apps as a whole.&lt;/p&gt;
&lt;h2&gt;Future&lt;/h2&gt;
&lt;p&gt;I really enjoyed working on this, and we&amp;#x27;re currently hiring. So contact me if this sounds exciting to you and you want to join us.&lt;/p&gt;
&lt;p&gt;Also check us out at &lt;a href=&quot;https://lovable.dev&quot;&gt;Lovable.dev&lt;/a&gt; or &lt;a href=&quot;https://x.com/emilahlback&quot;&gt;drop me a message directly&lt;/a&gt; if you&amp;#x27;d like to learn more.&lt;/p&gt;</content:encoded>
      <dc:creator>Emil Ahlbäck</dc:creator>
      <pubDate>Tue, 04 Mar 2025 13:01:00 GMT</pubDate>
    </item>
  </channel>
</rss>
