Skip to content

Convert TCX to ZWO

Turn a TCX (Training Center XML) workout into a Zwift ZWO file so a plan exported from a training platform can run in Zwift. Use the free, in-browser Kaiord Editor (no account, no upload), or the CLI and TypeScript SDK. Conversions go through Kaiord's canonical KRD format and stay within round-trip tolerances (time ±1 s).

Three ways to convert

1. Editor (drag & drop)

Open kaiord.com/editor, drop your .tcx file, choose ZWO as the export format, and download workout.zwo.

2. CLI

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

3. SDK

ts
import { fromText, toText } from "@kaiord/core";
import { tcxReader } from "@kaiord/tcx";
import { zwiftWriter } from "@kaiord/zwo";
import { readFile, writeFile } from "node:fs/promises";

const tcx = await readFile("workout.tcx", "utf-8");
const krd = await fromText(tcx, tcxReader);
const zwo = await toText(krd, zwiftWriter);
await writeFile("workout.zwo", zwo);

What survives the conversion

DataTCXZWO (Zwift)Result
Step order & names<Step> / <Name>blocksPreserved (names kept as kaiord: attributes)
Time durationsTime_t secondsDuration secondsPreserved (±1 s)
Distance durationsDistance_t meterstime-based DurationConverted to time; original kept under extensions.zwift
Power targets— (TCX has none)Power (% FTP)No power to carry — steps become FreeRide (see below)
Heart-rate targetsHeartRate_t (zone or bpm)Dropped (Zwift is power-based); kept under extensions.zwift
Speed targetsSpeed_tDropped (no Zwift equivalent); kept under extensions.zwift
Cadence targetsCadence_tlimitedPreserved under extensions.zwift
Repeats / intervalsRepeat_tIntervalsTPreserved

Gotchas

My steps became free rides. TCX workouts have no power target (the schema covers heart rate, speed, and cadence only), and Zwift workouts are power-based. With no power to map, the structured steps become FreeRide segments that keep their duration but have no power target. For a power-based Zwift workout, start from a source that carries power, such as FIT or Garmin Connect.

My heart-rate targets are gone. 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 → TCX round-trip can restore them.

Distance intervals became time intervals. ZWO durations are time-based. Distance durations are converted to time, and the original distance is preserved under extensions.zwift.