You've got a WordPress site with hundreds of pages built in Visual Composer (WPBakery). You need to move to Gutenberg. Manually converting each page is weeks of tedious copy-paste work β and you know half the formatting will break anyway.
vc2gut parses VC shortcodes into an AST, fingerprints the page structure, and converts to native Gutenberg blocks. Pages that share the same layout get the same fingerprint β accept one, batch-accept them all.
| Before | After |
|---|---|
[vc_row][vc_column width="1/2"]... |
<!-- wp:columns --><!-- wp:column -->... |
| Manually rewrite each page | Review and batch-accept by pattern |
| Weeks of migration work | Side-by-side review in minutes |
- π§ Smart Parsing β tokenizes
[vc_*]shortcodes into a full AST - π Structural Fingerprinting β groups identical layouts with xxhash for batch operations
- π¦ 30+ VC Elements β rows, columns, text, images, buttons, accordions, videos, grids, and more
- π Side-by-Side Review β original VC source (left) vs converted Gutenberg (right)
- β‘ Pattern Batch Accept β accept one page, batch-accept all pages with the same structure
- π Polylang Sync β auto-accepts translation pairs when structures match
- π Inline Editing β edit converted output before saving back to WordPress
- π Non-Destructive β original content preserved until you explicitly accept
git clone https://github.com/erimeilis/vc2gutenberg.git
cd vc2gutenberg
# Install dependencies
npm install
# Configure WordPress database connection
cp .env.example .env
# Edit .env with your DB credentials (or skip β auto-discovers wp-config.php)
# Start dev server (API + React UI)
npm run devOpen http://localhost:5173 and start reviewing.
The tool finds your WordPress database automatically:
.envfile in the project directory (recommended)wp-config.phpauto-discovery (walks up parent directories β works with Valet, MAMP, etc.)
# .env β Option A: Individual variables
WP_DB_HOST=localhost
WP_DB_PORT=3306
WP_DB_USER=root
WP_DB_PASSWORD=
WP_DB_NAME=your_wordpress_db
WP_TABLE_PREFIX=wp_
# Option B: Connection URL
WP_DATABASE_URL=mysql://root:password@localhost:3306/your_wordpress_db Scan Parse Fingerprint Convert Review Accept
ββββββββββ ββββββββββ βββββββββββββ ββββββββββββ βββββββββββ ββββββββββββ
β Read βββββΆβ Build βββββββΆβ Group by βββββΆβ VC β WP βββββΆβ Side by βββββΆβ Save to β
β WP DB β β AST β β structure β β blocks β β side β β WP DB β
ββββββββββ ββββββββββ βββββββββββββ ββββββββββββ βββββββββββ ββββββββββββ
β β
βββ Same fingerprint βββββββββββΆ Batch accept all
- Scan β reads all posts containing
[vc_shortcodes from your WordPress database - Parse β tokenizes shortcodes into an Abstract Syntax Tree
- Fingerprint β generates structural hash (xxhash) to group identical page layouts
- Convert β maps VC elements to native Gutenberg block markup
- Review β side-by-side UI for comparing original vs converted output
- Accept β saves converted content back to
post_contentin WordPress
| VC Shortcode | Gutenberg Block | Notes |
|---|---|---|
vc_row (single column) |
Unwrapped | Content promoted to top level |
vc_row / vc_row_inner (N cols) |
core/columns |
Column widths preserved |
vc_column_text |
Split into blocks | Headings, paragraphs, lists, tables |
vc_single_image |
core/image |
Flagged if matches featured image |
vc_separator / vc_text_separator |
core/separator |
|
vc_btn / vc_button |
core/button |
|
vc_video |
core/embed |
YouTube/Vimeo detection |
vc_message |
core/paragraph |
Preserves legacy CSS class |
vc_raw_html / vc_raw_js |
Decoded + split | Base64/URL-encoded content handled |
vc_tta_accordion / vc_tta_tabs / vc_tta_tour |
Flagged | Complex elements for manual review |
vc_basic_grid / vc_masonry_grid |
Flagged | Archive templates, flag for removal |
vc_empty_space |
Removed | Not needed in Gutenberg |
vc2gut/
βββ cli/ # Backend β Express 5 API + conversion engine
β βββ src/
β βββ parser/ # VC shortcode tokenizer + AST builder
β βββ fingerprint/ # Structural fingerprinting (xxhash)
β βββ converter/ # VC β Gutenberg block serializer + recipes
β βββ db/ # MySQL (WordPress) + SQLite (app state)
β βββ server/ # Express API (19 endpoints)
βββ ui/ # Frontend β React 19 + Vite
β βββ src/
β βββ components/ # ReviewQueue, SourcePreview, GutenbergEditor,
β β # ActionBar, Settings, Button, Layout
β βββ hooks/ # useApi, useAppConfig, useAnimatedNumber
β βββ i18n/ # Translations (English)
βββ data/ # SQLite database (auto-created at runtime)
| Database | Purpose |
|---|---|
| WordPress MySQL | Source of truth for posts β read during scan, write on accept |
| SQLite | App state β migration status, fingerprints, user edits, settings |
npm test # Run all 136 tests
npm test -- --watch # Watch mode| Suite | Tests | Covers |
|---|---|---|
| Parser (tokenizer + AST) | 14 | Shortcode tokenization, tree building |
| Converter (serializer + HTML splitter) | 40 | VC β Gutenberg block output |
| Recipes | 23 | Recipe-based conversion rules |
| Fingerprint | 7 | Structural hashing, grouping |
| Database | 19 | SQLite state, WP config parsing |
| Integration | 17 | End-to-end with real-world fixtures |
npm run dev # Start API (port 3001) + UI (port 5173)
npm run dev:cli # API server only (hot reload via tsx)
npm run dev:ui # React UI only (Vite)
npm run build # Production build (both workspaces)
npm run test # Run test suite (Vitest)
npm run lint # TypeScript type checking (both workspaces)| Layer | Technology |
|---|---|
| Runtime | Node.js 22+ |
| Backend | Express 5, TypeScript 5.9, mysql2, better-sqlite3 |
| Frontend | React 19, Vite, React Router 7, i18next |
| Testing | Vitest 4.0 |
| Monorepo | npm workspaces (cli/ + ui/) |
Switched Node versions after installing? Rebuild the native module:
npm rebuild better-sqlite3
# or clean install
rm -rf node_modules && npm installThe tool looks for credentials in order:
.envfile in project rootwp-config.phpin parent directories
Create one: cp .env.example .env
MIT β use it, fork it, ship it.