Skip to content

Convert FIT to ZWO

Turn a Garmin FIT workout into a Zwift ZWO file so a structured session from your head unit or coach can run in Zwift. The fastest way is the free, in-browser Kaiord Editor — no account and no file upload. Developers can use the CLI or the TypeScript SDK. Every conversion goes through Kaiord's canonical KRD format, so values stay within round-trip tolerances (time ±1 s, power ±1 W or ±1 % FTP).

Three ways to convert

1. Editor (drag & drop)

Open kaiord.com/editor, drop your .fit file, choose ZWO as the export format, and download workout.zwo. Everything runs locally in your browser.

2. CLI

bash
pnpm add -g @kaiord/cli
kaiord convert -i workout.fit -o workout.zwo

The format is detected from the file extensions. Add --input-format fit if your file has a non-standard extension.

3. SDK

ts
import { fromBinary, toText } from "@kaiord/core";
import { fitReader } from "@kaiord/fit";
import { zwiftWriter } from "@kaiord/zwo";
import { readFile, writeFile } from "node:fs/promises";

const buffer = await readFile("workout.fit");
const krd = await fromBinary(new Uint8Array(buffer), fitReader);
const zwo = await toText(krd, zwiftWriter);
await writeFile("workout.zwo", zwo);

What survives the conversion

DataFITZWO (Zwift)Result
Step order & namesworkout stepsSteadyState / Warmup / CooldownPreserved (names kept as kaiord: attributes)
Time durationssecondsDuration secondsPreserved (±1 s)
Distance durationsmeterstime-based DurationConverted to time; original meters kept in kaiord:originalDurationMeters
Power targetswatts / % FTP / zonePower (fraction of FTP)Normalized to % FTP (see FTP note below)
Heart-rate targetsbpm / zoneDropped (Zwift is power-based); kept under extensions.zwift
Cadence targetsrpmlimitedPreserved under extensions.zwift
Repeats / intervalsrepeat stepsIntervalsTPreserved

The Kaiord CLI logs a warning for every lossy step (for example Lossy conversion: heart rate target not supported by Zwift) so you can see exactly what changed.

Gotchas

Do I need my FTP? ZWO expresses power as a fraction of FTP. If your FIT targets are absolute watts, the conversion maps them to % FTP using a default FTP assumption — rescale in Zwift (or the Editor) if the exact watts matter. Zone- and %FTP-based FIT targets map across directly.

My heart-rate targets are gone. Zwift workouts are power-based and ZWO has no native heart-rate target, so HR targets are dropped from the visible workout. Kaiord preserves them under extensions.zwift so a later ZWO → FIT round-trip can restore them.

My distance intervals became time intervals. ZWO durations are time-based. Distance durations are converted to time and the original distance is preserved in kaiord:originalDurationMeters.