Systems

major system

Smart Contract Prettier for Remix

Local-first formatter plugin for Remix Desktop: versioned releases, Format on Save, and 27 smart-contract file extensions.

A formatter should feel native to the editor without sending contract source to a hosted service. I built a Remix iframe plugin that runs from a loopback-only local bundle, formats Solidity through Prettier, safely normalises other smart-contract syntaxes, and formats supported files when users save them.

scale
27 recognised smart-contract extensions. Solidity uses the native Prettier Solidity parser; 26 additional extensions use intentionally conservative structural formatters.
latency
Formatting happens locally in the Remix iframe after a short save debounce. No contract source is posted to a remote formatter service.
reliability
The plugin reads, formats, then writes only after formatting succeeds. Unsupported extensions are skipped, and Remix receives success, loading, and failure status events.
automation
Format on Save is enabled by default. Users can persist print width, indentation, tabs, bracket spacing, and optional Format on Open locally.
Official Prettier logofirst proof
Local-first Remix formatter with versioned release bundles and Format on Save.
Context

Problem had shape before code did.

After one local installation in Remix, the plugin watches the file manager's save event. It checks the selected extension, reads the file through Remix permissions, formats it locally, and writes it back only when the result differs. Solidity gets native Prettier formatting. Other supported smart-contract languages receive safe whitespace and indentation normalisation without the formatter changing unknown files.

  • Designed a local-first release path: GitHub Releases provide versioned ZIP bundles instead of a hosted formatter. The bundle starts a loopback-only server on 127.0.0.1 for Remix Desktop.
  • Connected Remix's `fileSaved` event to a debounced formatting queue, so users save normally instead of reopening the side panel for every document.
  • Used Prettier with the Solidity parser for `.sol` files and kept non-native language formatters structural and token-preserving rather than pretending every language has a full parser.
  • Added a stateful plugin surface: connection health, current-file detection, engine badge, persisted style preferences, formatted-file count, and Remix status feedback.
Host
Remix iframe plugin using the File Manager API and host permission model.
Distribution
GitHub Releases ZIP; users run a local 127.0.0.1 server and add it once to Remix.
Formatting guarantee
Native Prettier for Solidity; safe structural formatting for other declared extensions; unsupported files untouched.
Build

Structure had to survive job.

  • The iframe uses `@remixproject/plugin-iframe` to handshake with Remix and subscribe to `currentFileChanged` and `fileSaved` events.
  • A per-path debounce map collapses repeated save events. An active-format set prevents overlapping writes to the same file.
  • Formatter routing uses one extension registry. Solidity goes through Prettier and `prettier-plugin-solidity`; indentation, S-expression, assembly, and brace-aware engines cover the structural paths.
  • Release bundles contain built static files plus a Node HTTP server that binds only to `127.0.0.1`. A Git tag builds, tests, archives, and publishes the matching ZIP through GitHub Actions.
The formatter belongs near the source file, not behind a hosted endpoint. A small iframe plugin gives Remix event access while the local server supplies a stable origin for Desktop and browser users. The release workflow makes each installable bundle immutable and traceable. Formatter routing is intentionally tiered: native syntax support where available, token-preserving structural formatting elsewhere, and no changes for unknown files.
Working stack

JavaScript (ES Modules) / Vite / Prettier 3 / prettier-plugin-solidity / @remixproject/plugin-iframe / Node.js HTTP / GitHub Actions Releases

Evidence

Browser becomes part of argument.

Evidence viewerKeyboard arrows change proof
Production trace
fileSaved -> debounce by path -> readFile -> format -> writeFile when changed
Solidity: Prettier + prettier-plugin-solidity
Release: test -> build -> local bundle -> versioned ZIP asset

01 / 01Production trace

Decisions

Small cuts make system trustworthy.

  • Preferences are scoped to the plugin's local storage and restored when the iframe connects, avoiding project-source changes for editor preferences.
  • The formatter compares original and formatted text before calling Remix `writeFile`, preventing unnecessary writes and extra save loops.
  • The local server rejects paths outside the bundle root, serves explicit content types, and disables caching so a new release is not hidden behind stale browser assets.
  • The UI distinguishes native Prettier support from structural formatting so users know exactly which guarantee applies to the active language.
Pressure

What resisted, broke, stayed expensive.

Constraints in motion

  • Remix Web and Remix Desktop keep their sideloaded-plugin registries separately, so a browser installation does not appear automatically in the desktop app.
  • A Remix local iframe plugin needs a local HTTP origin; opening its static page directly is expected to remain in a connecting state because no Remix parent exists.
  • Many smart-contract languages lack a browser-ready Prettier parser. Formatting broadly while preserving source safety required separate conservative engines instead of one claimed universal parser.

Failure modes

  • A source file can be skipped when its extension is unsupported; the plugin does this deliberately instead of risking a destructive rewrite.
  • A local plugin cannot provide its own outer Remix sidebar icon. Remix currently assigns its fixed local-plugin image to every sideloaded iframe plugin.
  • The first read or write can require a Remix permission prompt. Until approved, auto-format reports the host error rather than silently claiming success.

Direct plugin page stayed in connecting state

  1. The formatter page was opened outside Remix during testing.
  2. Without an iframe parent, no Remix handshake or file context exists.
  3. Installation documentation now distinguishes direct preview from the Remix side-panel flow.

Repair: Ship a local bundle with explicit Remix connection steps and expose clear connection state inside the panel.

Held: Users start the loopback server, install it as a Remix iframe plugin, and then receive file context and save events.

Trade

  • Local release bundles require Node.js to run the loopback server, but they keep smart-contract source on the user's machine and avoid an always-on hosted deployment.
  • Only Solidity receives a language-native Prettier parser. The wider extension list uses structural formatting to preserve tokens rather than attempting syntax-aware rewrites without a parser.
  • Remix's custom outer sidebar icon would require an approved, hosted Plugin Directory listing. The project prioritises locally downloadable releases instead.
Result

What held. What carries forward.

plugin event design

Format on Save through Remix file events

The iframe subscribes to Remix `fileSaved`, debounces by path, and formats only recognised source extensions.

release workflow

Versioned local installation bundle

Tag releases run tests, build the Vite bundle, package a loopback server, and publish a version-specific ZIP asset.

local server design

No remote contract-source formatting service

The shipped server binds to 127.0.0.1 and the formatter runs in the local plugin iframe.

If rebuilt

  • Add parser-backed formatters as stable browser-compatible language packages become available.
  • Offer workspace-wide formatting with an explicit preview and per-file confirmation.
  • Package native launchers so end users can start the loopback service without a Node.js installation.
結論

Editor-like UX is mostly event design and trust boundaries. Auto-format only feels safe when it is debounced, avoids redundant writes, states which formatter engine is active, and never touches a file outside its declared support contract.

Make it hold together

Complex frontend system need steady hand?

Architecture, interface, production constraints. Together.