Feature Guide

Why We Chose Code Over Drag-and-Drop (And How LLMs Change the Equation)

January 2026 · 11 min read

Visual form builders are great until you need something that's not in the menu. "Show this field only when option A is selected AND the quantity is above 5" - good luck finding that in a dropdown. At some point, every drag-and-drop tool hits a wall. We decided to build for people who need what's on the other side.

This isn't an argument that code is always better. For a simple contact form with name, email, and message - visual builders work fine. But when you need calculated prices, conditional logic that branches three levels deep, or fields that validate against each other - you need more than what point-and-click can offer.

The traditional problem with code-based tools was the learning curve. You needed to be a developer or hire one. But that equation changed. LLMs can now write working form configurations from plain English descriptions. You describe what you want, the AI writes the code, and you paste it into the editor. The barrier just got a lot lower.

What Visual Builders Can't Do

Let's be specific about where drag-and-drop tools break down. These aren't edge cases - they're common requirements for anyone building forms for a real business.

Complex Conditional Logic

Most visual builders let you show or hide a field based on one condition. "Show field B if field A equals 'yes'". That covers maybe 20% of real use cases.

What about "show the deposit field only if the service type is 'wedding' AND the date is less than 30 days away AND the total exceeds $500"? That's three conditions that all need to be true. In most visual tools, you can't express this at all. In code, it's one line:

isVisible: () =>
    service.value() === 'wedding' &&
    daysUntil(date.value()) < 30 &&
    total() > 500

The logic is right there. Readable. Changeable. No hunting through nested dropdown menus trying to figure out which rule is breaking.

Calculated Values

A cleaning company wants to show a price estimate. The formula: base rate times square footage, plus extra for bathrooms, minus a discount for recurring service, adjusted by a location multiplier. Try building that in a visual editor.

In code, you write the formula:

computedValue: () => {
    const base = squareFeet.value() * 0.12;
    const bathrooms = bathroomCount.value() * 25;
    const discount = isRecurring.value() ? 0.85 : 1;
    const location = locationMultipliers[zip.value()] || 1;

    return (base + bathrooms) * discount * location;
}

Change any input - square footage, bathroom count, recurring toggle, zip code - and the price updates instantly. No refresh, no "calculate" button. It just works.

Reactive Updates

This is where code-based forms really shine. When you write a function instead of a static value, that function re-runs automatically whenever its inputs change.

In FormTs, almost every configuration property can be either a value or a function. We call this pattern ConfigValue:

// Static - never changes
label: 'Your Name'

// Reactive - updates when dependencies change
label: () => 'Price for ' + selectedPlan.value()

Labels, visibility, validation rules, options lists, min/max constraints - anything can be reactive. Change one field and watch a dozen other things update in response. No event handlers to wire up. No state management to debug. The form just stays in sync.

Pro tip

Reactive doesn't mean complicated. You only make something reactive when it needs to be. A field label that never changes stays a plain string. A price that depends on three other fields becomes a function. Use what you need.

Fields That Depend on Other Fields

"This field is required only for weddings." "End date can't be before start date." These dependencies are common but hard to express in visual builders.

In code, any field property can reference other fields:

// Required only when service type is 'wedding'
isRequired: () => serviceType.value() === 'wedding'

// End date can't be before start date
minDate: () => startDate.value()

The constraints update automatically when the referenced fields change. Select "wedding" and suddenly the field becomes required. Pick a start date and the end date picker won't let you choose anything earlier.

What The Code Actually Looks Like

Let's be honest about this. It's TypeScript. There are functions, curly braces, arrow syntax. If you've never written code before, your first look will feel unfamiliar. But unfamiliar isn't the same as complicated.

Here's a real example - a section from a hair salon booking form:

const colorSection = form.addSubform('color', {
    title: 'Color Services'
});

colorSection.addRow(row => {
    row.addCheckbox('colorService', {
        label: 'Color Service',
        defaultValue: false
    });
});

colorSection.addRow(row => {
    row.addDropdown('colorType', {
        label: 'Color Type',
        options: [
            { id: 'root-touch-up', name: 'Root Touch-Up' },
            { id: 'full-highlights', name: 'Full Highlights' },
            { id: 'balayage', name: 'Balayage/Ombre' }
        ],
        defaultValue: 'root-touch-up',
        isVisible: () => colorSection.checkbox('colorService')?.value() === true
    });
});

Breaking it down:

  • addSubform creates a collapsible section with a title
  • addRow creates a horizontal row of fields
  • addCheckbox adds a checkbox with a label and default value
  • addDropdown adds a dropdown with options
  • isVisible: () => ... makes the dropdown appear only when the checkbox is checked

The pattern repeats. Add a section. Add rows. Add fields to rows. Configure each field with properties. Make properties reactive when needed. Once you see the pattern, you can read most form configurations even without deep TypeScript knowledge.

See more form examples in our calculator gallery.

You Don't Have to Write It From Scratch

Here's where things get interesting. You don't need to memorize syntax or learn TypeScript from a textbook. You can describe what you want in plain English and let an LLM write the code.

You have two options: use our built-in AI editor, or bring your own LLM.

Option 1: Built-in AI Editor

The FormTs editor has AI built right in. You type what you want - "add a dropdown for service type with options for haircut, color, and styling" - and watch the form build itself in real time. The AI writes the code, the preview updates live, and you see exactly what you're getting.

You can buy tokens directly from us to use the built-in AI. It understands FormTs completely - every field type, every configuration option, every pattern.

We're confident LLMs handle FormTs well because we built most of our example gallery with their help. The calculators you see in the gallery - cleaning quotes, auto detailing, hair salons - were largely generated by AI and then refined. It works.

Option 2: Use Your Own LLM

If you'd rather use ChatGPT, Claude, or another LLM directly, we publish our complete type definitions for exactly this purpose. Copy them, paste them into your conversation, and the AI understands the full FormTs API.

The External LLM Workflow

Step 1: Go to our types page and copy the type definitions.

Step 2: Paste them into your LLM conversation. Say something like: "Here are the type definitions for a form builder called FormTs. I'll ask you to generate forms using these types."

Step 3: Describe what you want in plain English:

"Create a quote form for a cleaning company. Include fields for square footage, number of bedrooms and bathrooms, cleaning type (standard, deep clean, move-out), and whether it's recurring. Show a calculated price that updates as they fill out the form. Price should be $0.12 per square foot, plus $25 per bathroom, with a 15% discount for recurring service."

Step 4: The LLM generates working TypeScript code. Copy it.

Step 5: Paste it into the FormTs editor. The form appears. Test it. If something's not right, either tweak the code yourself or ask the LLM to adjust it.

Pro tip

Be specific in your prompts. "A contact form" gives generic results. "A contact form for a law firm with fields for case type, urgency level, and preferred contact method, where urgency shows only for certain case types" gives you something useful.

When You Need to Modify

LLM-generated code isn't always perfect on the first try. Sometimes the logic is slightly off, or you want to add something the AI didn't include. This is where basic code literacy helps.

You don't need to be a developer. But understanding what the code does lets you make small changes: adjust a price multiplier, add an option to a dropdown, change a label. The syntax is consistent enough that once you've seen a few examples, you can pattern-match your way through modifications.

And if you get stuck, paste the code back into the LLM and ask for help. "This form calculates price wrong when recurring is selected. The discount should be 15%, not 50%. Fix it." The AI can debug its own output.

The Editor Catches Mistakes

One advantage of TypeScript: errors show up before you run anything. Misspell a property name? Red underline. Pass a string where a number is expected? Red underline. Try to use a field type that doesn't exist? Red underline.

The FormTs editor includes full TypeScript checking. When the AI generates code with a typo or invalid configuration, you see the error immediately. No waiting until runtime to discover something's broken.

This matters more than it sounds. Visual builders fail silently - you configure something wrong and the form just doesn't work, with no indication why. Code-based tools with proper typing tell you exactly what's wrong and exactly where.

Code Is Text, and Text Has History

Here's an advantage of code that visual builders can never match: code is just text. And text can be compared, versioned, and tracked.

When you build forms visually, there's no good way to see what changed between versions. You click around, make changes, save. What was different before? What exactly did you modify? You don't know unless you remember.

With code, every change is visible. FormTs includes a built-in version history browser. Every time you save - whether you edited manually or the AI made changes - the previous version is preserved. You can open any historical version and compare it to the current code in a side-by-side diff view.

The diff highlights exactly what changed: lines added, removed, modified. If the AI broke something, you see precisely which lines it touched. If you want to understand what changed between version 5 and version 12, you can compare them directly. If you want to restore something from three days ago, it's there.

This becomes invaluable when you're iterating with AI. You ask for a change, the AI modifies the code, you see exactly what it did. Don't like it? You know exactly what to revert. Want to keep part of the change but not all? You can see line by line what's different.

Sharing Is Copy-Paste

Another advantage of code being text: you can share it anywhere. Send a form configuration over WhatsApp, Slack, email, or paste it in a forum post. The recipient copies it, pastes it into the FormTs editor in their browser, and the form runs instantly.

No export buttons, no file downloads, no account sharing. Just text that works. Want to show a colleague how you solved a tricky pricing calculation? Copy the relevant section and send it. They can run it, modify it, learn from it - all without any setup.

What Else Code Gets You

Beyond the main advantages, code brings a few smaller benefits that add up over time.

Reusable Logic

Within a form, you can extract common patterns into functions. Need the same price calculation in three places? Write it once, call it wherever you need it. The same discount logic, the same date validation, the same conditional check - defined once, used everywhere in your form.

// Define once
const applyDiscount = (price: number) =>
    isFirstTime.value() ? price * 0.85 : price;

// Use everywhere
computedValue: () => applyDiscount(basePrice())
computedValue: () => applyDiscount(addonTotal())

Comments That Explain Why

Code lets you add comments. Not just what a field does, but why it works that way. The business context lives right next to the code it explains. Six months later, you'll know why that weird exception exists.

// Wedding bookings under 30 days require 25% deposit
// per insurance policy updated March 2024 after
// two last-minute cancellations cost us $4k
isVisible: () =>
    eventType.value() === 'wedding' &&
    daysUntil(eventDate.value()) < 30

Try storing that context in a visual builder. You can't. The reasoning behind your business rules gets lost, and six months later someone changes that "arbitrary" 25% thinking it's a mistake.

Search Everything

"Where do we apply the 15% discount?" In a visual builder, you'd click through every form, every field, every rule. In code, you search for "0.85" or "discount" and find every instance in seconds. Same for finding all fields that reference a specific value, all places where a certain validation runs, all forms that use a particular pattern.

IDE Superpowers

Modern code editors do more than syntax highlighting. Autocomplete suggests valid options as you type - field types, configuration properties, available methods. Hover over a function to see what it expects. Click "go to definition" to jump to where something is defined. These tools make writing and navigating code faster than clicking through menus.

When Visual Builders Are Fine

We're not saying code is always the answer. For some forms, drag-and-drop is perfectly adequate:

  • Simple contact forms (name, email, message)
  • Newsletter signups (just an email field)
  • Basic surveys without branching logic
  • Forms where you don't need calculated values or complex validation

If your form is "collect some fields and send an email," you don't need the power of code-based configuration. Use whatever's fastest.

But the moment you think "I wish I could..." - show a field conditionally, calculate a price, validate against another field, change options based on previous answers - that's when visual builders start fighting you and code starts helping you.

Getting Started

If you want to try this approach:

1. Look at examples. Browse the calculator gallery. Open any calculator and view its source code. See how real forms are structured.

2. Try the LLM workflow. Copy our type definitions, paste them into your favorite AI chat, and describe a form you need. See what comes out.

3. Experiment in the editor. Paste generated code into the FormTs editor. Make small changes. See what breaks, what works. The editor validates as you type.

The learning curve isn't as steep as "learn to code." It's more like "learn to read configurations and make small edits." For most people, that takes hours, not months.

Common Questions

Do I need to know TypeScript to use FormTs?

Not from scratch. You can use LLMs to generate form code from plain English descriptions. But basic familiarity helps when you need to make adjustments. If you can read the code and understand what each part does, you can modify it. Most people pick this up quickly by looking at examples.

What if the AI generates code that doesn't work?

The editor shows errors immediately - red underlines on anything that's wrong. You can either fix simple issues yourself (typos, wrong values) or paste the code back into the AI and ask it to fix the specific problem. 'This line has an error, the price calculation is wrong' usually gets you a working fix.

Is this harder than drag-and-drop?

For simple forms, yes - dragging is faster than writing code. For complex forms with logic and calculations, it's actually easier. You spend less time fighting the tool's limitations and more time describing what you want. The total time is often less, especially once you're familiar with the patterns.

Can I switch between visual editing and code?

FormTs is code-first. There's no visual editor that generates code. But you can preview your form in real-time as you edit code, so you immediately see what your changes do. Many users find this faster than switching between edit and preview modes in visual tools.

Ready to Try Code-Based Forms?

Copy our type definitions, describe what you need to an LLM, and see what's possible.