# Zero-Friction Migration: How to Import Your Existing Developer Blog into ZyVOP

Technical writing is one of the highest-leverage assets in software engineering. Over time, however, developer portfolios become fragmented: early articles on **DEV**, corporate posts on **Medium**, a custom domain on **Hashnode**, or Markdown files in static site generators like **Hugo**, **Astro**, or **Ghost**.

Switching platforms often leads to broken syntax highlighting, mangled math formulas, orphan image references, duplicate cover photos, and **canonical SEO dilution**.

ZyVOP was built as a sovereign developer publishing engine: **write once, maintain canonical ownership, and distribute across ecosystems**. But migrations are never one-size-fits-all.

This guide provides an honest, technical look at how ZyVOP's import engine works, what is natively supported versus partially adapted, and how to migrate your catalog cleanly.

* * *

## 1\. Migration Support Matrix

Not every platform exposes identical APIs or export formats. ZyVOP supports migrations through four distinct ingestion pipelines:

| Source | Migration Pathway | Level of Support | Key Technical Nuances |
| --- | --- | --- | --- |
| DEV.to | Native REST API | First-Class | Pulls published articles, markdown body, tags, canonical URLs, and original dates. |
| Hashnode | GraphQL API / Backup JSON | First-Class | Supports custom publication domains, backup JSON files, and RSS fallback. |
| Medium | Public RSS / Single URL | Partial | Ingests via public user feed; image re-hosting is disabled by default to avoid rate limits. |
| Ghost | Generic RSS / Single URL | Partial | Full text via RSS content:encoded. Native Ghost JSON archives are not yet parsed. |
| Substack | Generic RSS / Single URL | Partial | Imports published posts via RSS. Subscriber lists and paid tiers are not imported. |
| Hugo / Jekyll | Markdown ZIP Archive | Partial | Ingests .md and .markdown files with relative image re-hosting. Shortcodes require flattening. |
| Astro / Next.js | Markdown ZIP Archive | Partial | Supports standard Markdown. .mdx files and JSX/Astro components are ignored. |

* * *

## 2\. Ingestion Pipelines: How It Works Under the Hood

![Mermaid Diagram](https://mermaid.ink/img/pako:eNptVE1P4zAQ_SuzPqAi0naBPfWwUukH7YpCNgFWIuYwTaaJVceObKelQP_7yi2UtiKXZGy_N2_ejPPGUp0R67CZ1Mu0QOPgvs8VAICtp7nBqoCxysk6oRUknJ23fGzIWghFRVIo4ux5i_BPf_CYcNYfPEI0iO-hG445e4Zm87f_9DvCUOp8ACEaS-YAPbpNOBuhLZTOCK59-r830IY_8d3tHs8XIIrjhLMojmFIlFloXBfaOmhDXE-tw3QObZhQJury9BPvD25TfyAvWj-hDV2nSxio_Ligf4OrhLNYqFwSdI0TqSR4iG4-6SLCDKdCCrdKOJvoVyEl7q_CGfTvJjAU0h1V-zQOPQTNPNNL5UNojOpcezXWGb2T_CQqT7hR3DVpIRYEgxdnMHXaQKNVZtCGVvlBdLpLQirj6qibodEpWStU7tt50YJbbUqU4hU3LT6BrrXkvnPC98yr8YCEs55WjpTb4Y-K-7J5B9pr2549fjcu0FQJZyGZlCpXo4SeXpCBPmV1JUW6FdfYnNvZcsi5M2k7Jb6KiJZGOPJ9JonO27atLqKmnxNvQiO-bPekrrNvaL_zL3baYE7evMsW9NHhFC3BCfRQaSVSlBCvVHrghWfcUG_KeeOs_1EUQa-gdM65anzBHyI_8vfCSTrlbL13tzza87zf0hJCbd079A3OnB9QXFC2jeBsT0toyJJZbPw7vKg7ssGLsM6-QzwXvgf-VVG2P0QsYCWZEkXGOm_MFVT6X0ZGM6ylY8F25RGNwKkk68_MtHJDLIVcsQ5rYlVJatqVdVQGcCWFmk8wjTfxUCsXAGcx5ZrgYcxZAJGeaqcDGJFckBMpBtA1AmUAFpVtWjJixoJNkli8ei3nv6oXtl4HbJr3tNSGddiPZSEcsfV_vtCLhQ?type=png)

### A. Direct APIs (DEV & Hashnode)

-   **DEV.to:** Connects via username or API key. Extracts raw Markdown directly, preserving original syntax fences, tags, and canonical links.
    
-   **Hashnode:** Queries Hashnode's GraphQL endpoint across either personal subdomains (`user.hashnode.dev`) or custom publication apex domains. Also accepts native Hashnode JSON export backups (`readHashnodeJson`).
    

### B. Universal RSS / Atom (Ghost, Substack, Medium)

-   Parses standard RSS 2.0, Atom, and `content:encoded` payloads.
    
-   **Image Re-hosting Nuance:** For bulk RSS imports, image re-hosting is disabled by default (`migrateImages = false`) to prevent timeout failures and remote CDN rate-limiting across large archives. Remote image URLs are preserved as absolute URLs.
    

### C. Direct Web Article Extractor

-   For standalone articles, ZyVOP executes an outbound fetch with Mozilla Readability, stripping boilerplate noise (`.cookie-banner`, `.advertisement`, `.newsletter-signup`, `nav`, `footer`).
    
-   **Perceptual Image Deduplication (Sharp):** Many CMSs duplicate the social `og:image` as the first image in the post body. The single-URL extractor creates a 32×32 sRGB luminance fingerprint of the cover image and compares it against body images:
    

```typescript
// Bounded sRGB luminance and chromatic mean comparison in article-import-images.ts
async function createImageFingerprint(data: Buffer): Promise<ImageFingerprint> {
  const pixels = await sharp(data, { limitInputPixels: 40_000_000 })
    .resize(32, 32, { fit: 'fill' })
    .toColourspace('srgb')
    .removeAlpha()
    .raw()
    .toBuffer();
  // Evaluates luminance array and chromatic delta to strip duplicate hero images
}
```

### D. Static-Site Markdown Archives (ZIP)

When migrating from static site generators like Hugo, Astro, Jekyll, or Obsidian:

-   **Supported Formats:** The reader accepts `.md` and `.markdown` files. Files with `.mdx` extensions or custom templates (`.astro`, `.html`) are ignored.
    
-   **Asset Re-hosting:** References like `./images/diagram.png` or `../assets/flowchart.webp` are normalized through `resolveArchiveAssetPath`, uploaded to persistent storage, and rewritten in the Markdown body.
    
-   **Frontmatter Ingestion:** Maps `title`, `canonical_url` (or `canonicalUrl`, `url`), `cover_image`, `tags`, and `published_at`.
    

* * *

## 3\. Duplicate Detection: Preventing Content Overwrites

ZyVOP avoids duplicate drafts during repeated syncs or CLI runs. Rather than relying on fragile hashes, the database executes a scoped uniqueness query:

```typescript
// Scoped duplicate check in markdown-zip-import.service.ts
if (canonicalUrl) {
  const normalized = canonicalUrl.replace(/[?#].*$/, '').replace(/\/+$/, '');
  query.andWhere(
    '(post.canonicalUrl = :canonicalUrl OR post.canonicalUrl = :normalized OR post.canonicalUrl = :slash)',
    { canonicalUrl, normalized, slash: `${normalized}/` },
  );
} else {
  query.andWhere('LOWER(TRIM(post.title)) = LOWER(TRIM(:title))', { title });
}
```

If an article matches an existing post owned by the author, it is safely skipped rather than duplicated or overwritten.

* * *

## 4\. How to Migrate: Step-by-Step

### Option 1: Web Dashboard (Zero Installation)

1.  Go to **Dashboard → Migrate** (`/migrate`).
    
2.  Select your source:
    
    -   **DEV.to:** Enter your username (`devto/yourusername`).
        
    -   **Hashnode:** Enter your publication domain (`blog.yourdomain.com`).
        
    -   **Medium / Ghost / Substack:** Paste your public profile or RSS feed URL (`https://yourblog.com/rss.xml`).
        
    -   **Single URL:** Paste a single article link to test readability extraction.
        
3.  Preview extracted articles, tags, and publication dates before committing them as drafts.
    

### Option 2: Uploading a Static Site ZIP

1.  In your Hugo/Astro/Jekyll repository, locate your Markdown directory.
    
2.  Ensure posts use standard Markdown (`.md` or `.markdown`). Convert `.mdx` component imports into standard code fences or markdown blocks.
    
3.  Zip the directory containing your posts and local image assets:
    
    ```bash
    zip -r blog-migration.zip content/posts/ assets/images/
    ```
    
4.  Upload via **Dashboard → Import → Upload Markdown ZIP**.
    

### Option 3: Terminal Migration via the CLI (`zyvop import`)

For developers who prefer terminal workflows:

```bash
# Authenticate
npx zyvop login

# Import from DEV
npx zyvop import devto yourusername

# Import from Hashnode
npx zyvop import hashnode blog.yourdomain.com
```

* * *

## 5\. Preflight Audits: The ZyVOP Compatibility Lab

Before publishing or cross-posting imported drafts, inspect them in the **ZyVOP Compatibility Lab** (`/compatibility-lab`).

The Compatibility Lab parses your article in-browser without modifying the database and runs preflight checks across destinations:

-   **Tag Truncation:** Flags when your post exceeds destination limits (4 tags on DEV, 5 on Hashnode and Medium).
    
-   **Social Announcement Limits:** Checks title and excerpt lengths against announcement constraints (180 characters for Bluesky, 320 for Mastodon).
    
-   **Local Asset Detection:** Warns if an imported article still references non-public image paths (`localhost` or un-migrated relative paths).
    
-   **Syntax Adaptation:** Reports how Mermaid diagrams and KaTeX formulas will adapt across each destination.
    

* * *

## 6\. Post-Migration: Establishing Sovereign GitOps

Once imported into ZyVOP, your articles are no longer locked into any single platform:

1.  **Sovereign Canonical URL:** Your ZyVOP publication acts as the primary authoritative source (`<link rel="canonical">`).
    
2.  **Multi-Platform Distribution:** Syndicate updates to DEV, Hashnode, Medium, and WordPress with one click or via `npx zyvop publish`.
    
3.  **Automated Git Backup:** Connect your GitHub account to have every post automatically committed to your personal repository via GitHub's Git Database API.
    

Take sovereign ownership of your technical writing today without losing your hard-earned SEO equity or past article catalog.

---

*Published via [ZyVOP](https://zyvop.com/zero-friction-migration-how-to-import-your-existing-developer-blog-into-zyvop-huck5?utm_source=hashnode&utm_medium=crosspost&utm_campaign=syndication) — Write once in Markdown, auto-backup to GitHub, and syndicate to Dev.to, Medium & Hashnode in 1 click.*
