Skip to content

Convert TCX to ZWO ​

Scope

Converts structured workouts (steps and targets), not recorded activities. See Workouts vs. activities.

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 (in the browser) ​

Open kaiord.com/app, choose your .tcx file (ZWO is already selected 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_t—Dropped (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.