Migration Guide: v1 to v2

April 1, 2026 ยท View on GitHub

Why Migrate

Inky v2 is a ground-up rewrite in Rust with significant improvements:

  • 10-100x faster -- Rust core compiles to native binary, WASM, or shared library
  • Cleaner syntax -- attributes over classes, singular <column>, sm/lg shorthand
  • New components -- video, hero, social, card, alert, badge, accordion, blockquote, preview
  • CLI toolchain -- inky build, inky watch, inky validate, inky init
  • CSS inlining -- built-in, enabled by default
  • Framework CSS -- SCSS framework with variable overrides, injected automatically
  • Layouts and includes -- <layout>, <include>, template variables
  • Validation -- catches missing alt text, Gmail clipping, Outlook issues
  • Template-friendly -- auto-detects Handlebars, ERB, Jinja2, Mailchimp, etc.
  • Cross-platform -- one engine powers CLI, Node.js, PHP, Python, and Ruby

Running the Migrator

Preview changes (stdout)

inky migrate email.inky

Migrate a directory

inky migrate src/ -o migrated/

Rewrite files in-place

inky migrate src/ --in-place

Programmatic migration (any language)

const inky = require("inky");
const result = inky.migrateWithDetails(v1Html);
console.log(result.html);      // migrated HTML
console.log(result.changes);   // ["<columns> -> <column>", ...]

Complete Migration Table

v1 Syntaxv2 SyntaxNotes
<columns><column>Plural to singular
</columns></column>Closing tag too
<h-line><divider>Renamed
large="6" on <column>lg="6"Shorthand
small="12" on <column>sm="12"Shorthand
<spacer size="16"><spacer height="16">Clearer attribute name
<spacer size-sm="10"><spacer sm="10">Shorthand
<spacer size-lg="20"><spacer lg="20">Shorthand
<button class="small"><button size="small">Class to attribute
<button class="alert"><button color="alert">Class to attribute
<button class="expand"><button expand>Class to bare attribute
<button class="radius"><button radius>Class to bare attribute
<button class="rounded"><button rounded>Class to bare attribute
<button class="hollow"><button hollow>Class to bare attribute
<callout class="primary"><callout color="primary">Class to attribute
<menu class="vertical"><menu direction="vertical">Class to attribute
<center><menu>...</menu></center><menu align="center">...</menu>Wrapping to attribute

Button size values

tiny, small, large

Button/callout color values

primary, secondary, success, alert, warning

Button boolean attributes

expand, expanded, radius, rounded, hollow

Before/After Examples

Basic layout

v1:

<container>
  <row>
    <columns large="6" small="12">Left</columns>
    <columns large="6" small="12">Right</columns>
  </row>
</container>

v2:

<container>
  <row>
    <column lg="6" sm="12">Left</column>
    <column lg="6" sm="12">Right</column>
  </row>
</container>

Button with styles

v1:

<button class="small alert expand" href="#">Click Me</button>

v2:

<button size="small" color="alert" expand href="#">Click Me</button>

Mixed classes (custom classes preserved)

v1:

<button class="small alert custom-btn" href="#">Click</button>

v2:

<button class="custom-btn" size="small" color="alert" href="#">Click</button>

Custom CSS classes that aren't migration targets are preserved in the class attribute.

Full template

v1:

<container>
  <row>
    <columns large="6" small="12">
      <button class="small alert" href="#">Click</button>
      <spacer size="16"></spacer>
      <h-line></h-line>
      <callout class="primary">Important</callout>
    </columns>
    <columns large="6" small="12">
      <center>
        <menu class="vertical">
          <item href="#">Link</item>
        </menu>
      </center>
    </columns>
  </row>
</container>

v2:

<container>
  <row>
    <column lg="6" sm="12">
      <button size="small" color="alert" href="#">Click</button>
      <spacer height="16"></spacer>
      <divider></divider>
      <callout color="primary">Important</callout>
    </column>
    <column lg="6" sm="12">
      <menu align="center" direction="vertical">
        <item href="#">Link</item>
      </menu>
    </column>
  </row>
</container>

Breaking Changes

  1. v2 parser is strict -- it does not accept v1 syntax. Run inky migrate first. If v1 tags are encountered, the parser outputs an error pointing to inky migrate.

  2. .inky file extension -- Source templates should use .inky. The CLI auto-generates .html output files. Both .inky and .html are accepted as input.

  3. CSS inlining is on by default -- v1 had no built-in inlining. If you handle inlining separately, pass --no-inline-css.

  4. Framework CSS is injected by default -- The built-in SCSS framework is compiled and injected into each email. Disable with --no-framework-css if you use your own CSS.

  5. role="presentation" on all layout tables -- v2 adds accessibility attributes to all generated tables. This is a non-breaking output change but may affect CSS selectors or snapshot tests.

Tips

  • Run inky validate after migration to catch any remaining issues.
  • The migrator is safe to run multiple times -- already-migrated syntax is left unchanged.
  • Use inky migrate --in-place with version control so you can review the diff.
  • The migrateWithDetails() API returns a list of changes made, useful for logging or reports.