LandingPager | Automata
sections (9)
$ cd .. // back to projects
automata@latam: ~/projects/landingpager
PRODUCT · slug: landingpager ·2026 · 2 min read · 432 words

> LandingPager

// Visual page builder with drag-and-drop grid editor: pick a layout, drop components into cells, publish to a public slug.

Laravel 12Livewire 3Flux UITailwind 4Alpine.jsMySQLPest 4
forks last commit
LandingPager
▸ role
Full-stack · Laravel · Livewire
▸ team
Solo
▸ status
ready
// section 01 · discovery

$ cat ./discovery.md

▸ overview

A page builder where non-technical users compose responsive web pages by selecting a layout (full-width, sidebar left/right, two-column) and dragging components into a CSS grid. Each component stores its grid_position (row, col, rowspan, colspan) as JSON on PageComponent, so the editor is just a Livewire view over the database. Pages publish to /p/{slug} with no static export step.

▸ problem

Existing builders are either expensive (Webflow, Framer) or oversimplified (Wix-style fixed templates). A grid-based editor with reusable components hits the middle: easier than HTML/CSS, more flexible than templates.

▸ audience

Small businesses, agencies and non-technical content creators who need to publish landing pages without paying for a SaaS or hiring a developer for each iteration.

// section 03 · architecture

$ cat ./architecture.md

// section 03b · sequences

$ cat ./sequences.md

// runtime flows — who talks to whom, in what order

// Browser drag is captured by Alpine.js and the final selection is sent to Livewire in one event.

Drag selection — to multi-cell component · flow-01
loading…
// section 04 · infrastructure

$ cat ./infrastructure.md

▸ services
provider: Laravel + MySQL + Vite
  • Laravel 12 app (HTTP + Livewire components)
  • MySQL 8 (pages, page_components, templates)
  • Vite dev server (HMR for Tailwind/Alpine)
  • Laravel queue + cache tables (database driver)
  • Laravel Fortify (auth + 2FA optional)
  • GitHub Actions FTP deploy
// section 05 · implementation

$ cat ./implementation.md

▸ frontend
  • · Livewire 3 + Volt 1.7
  • · Flux UI
  • · Tailwind CSS 4
  • · Alpine.js
  • · Vite
▸ backend
  • · Laravel 12
  • · PHP 8.2
  • · Laravel Fortify 1.30
▸ data
  • · MySQL
  • · Eloquent + soft deletes
  • · JSON columns for grid_position and component settings
▸ devops
  • · GitHub Actions (deploy.yml — FTP)
  • · Concurrently for parallel dev (artisan + vite + queue)
// section 06 · technical challenges

$ cat ./challenges/*.md

// 1 technical problems solved

01 / 01
challenge-01.md · grid · livewire · alpine
▸ problem

Placing a component spanning multiple cells from a drag selection

constraint: The editor is rendered server-side by Livewire, but the drag selection happens in the browser. Sending one event per pointer move would flood the server; sending only the final selection means Livewire must derive row/col/colspan from two cell coordinates.

▸ approach

Alpine.js tracks the drag locally and dispatches a single Livewire event with selectionStart + selectionEnd when the pointer is released. PageEditor::addComponentToCellFromAlpine() derives row/col/colspan from min/max of the two points and persists a PageComponent row with the grid_position as JSON.

app/Livewire/PageEditor.php php
public function addComponentToCellFromAlpine($type, $selectionStart, $selectionEnd)
{
    $row     = min($selectionStart['row'], $selectionEnd['row']);
    $col     = min($selectionStart['col'], $selectionEnd['col']);
    $colEnd  = max($selectionStart['col'], $selectionEnd['col']);
    $colspan = ($colEnd - $col) + 1;

    PageComponent::create([
        'page_id'  => $this->page->id,
        'type'     => $type,
        'settings' => [
            'grid_position' => [
                'row' => $row, 'col' => $col,
                'rowspan' => 1, 'colspan' => $colspan,
            ],
        ],
    ]);
}
// section 07 · testing & ci

$ cat ./testing.md

▸ strategy

Pest 4 with the Laravel plugin. Test DB is SQLite in-memory (no MySQL required in CI). Coverage focuses on page persistence and component placement.

▸ tools
Pest 4.3PHPUnitPest Laravel plugin
// section 09 · results

$ cat ./results.md

01 /
4
Eloquent models (Page, PageComponent, Template, User)
02 /
5
Livewire components (editor, layout/type selectors)
03 /
8
migrations
04 /
2
seeders (Database + Template)
▸ outcomes

MVP: page creation, grid-based editor, layout selection and public preview routes are in place. Deploys via FTP on push to main. Public template library is the next milestone.

// related

$ grep -l "tech" ./projects/*

// projects that share part of the stack

// next step

$ automata deploy --your-operation

// Let's talk about adapting this to your case.

./let-s-talk.sh