Flight Card

Pro

A flight an assistant found or booked, inside a chat reply: the airline, both ends of the route with the line and stops between them, the fare, and once it is booked, the live status, gate and seat.

UAUnited AirlinesEconomy · 1 carry-on
Best
Outbound · Thu, Jun 18 · UA 1532
8:15 AMLAXLos Angeles
2h 35mNonstop
11:50 AMDENDenver
Return · Sun, Jun 21 · UA 2291
6:40 PMDENDenver
2h 15mNonstop
7:55 PMLAXLos Angeles
$436Round trip per person

Installation#

# One-time setup (tokens + lib helpers)npx koalaui-cli@latest init# Activate your Pro license (once per machine)npx koalaui-cli@latest login <key># Add this component (its dependencies come along)npx koalaui-cli@latest add flight-card

Usage#

This example is part of Koala UI Pro

The preview is yours to explore. Its code comes with a Pro license, paid once and used in every project.

Have a license? Unlock it here

Times, dates and prices go in already formatted, in the airport's local time. Format them where you know the traveller's locale and the airport's time zone, with Intl.DateTimeFormat("en-US", { timeZone: "America/Denver" }), so the server and the browser print the same thing.

Anatomy#

A header with the airline, the route as the core, and a footer with the fare and the actions. Every part is droppable: a result row needs no actions, a status update needs no price.

import {  FlightCardList,  FlightCard,  FlightCardHeader,  FlightCardAirline,  FlightCardTag,  FlightCardStatus,  FlightCardLeg,  FlightCardRoute,  FlightCardEndpoint,  FlightCardPath,  FlightCardStop,  FlightCardDetails,  FlightCardDetail,  FlightCardFooter,  FlightCardPrice,  FlightCardActions,  FlightCardSkeleton,} from "@/components/ui/flight-card"<FlightCardList>  <FlightCard>    <FlightCardHeader>      <FlightCardAirline />      <FlightCardTag />      <FlightCardStatus />    </FlightCardHeader>    <FlightCardLeg>      <FlightCardRoute>        <FlightCardEndpoint />        <FlightCardPath>          <FlightCardStop />        </FlightCardPath>        <FlightCardEndpoint />      </FlightCardRoute>    </FlightCardLeg>    <FlightCardDetails>      <FlightCardDetail />    </FlightCardDetails>    <FlightCardFooter>      <FlightCardPrice />      <FlightCardActions />    </FlightCardFooter>  </FlightCard>  <FlightCardSkeleton /></FlightCardList>

Stops and legs#

Each FlightCardStop sets its airport code on the line, and the caption under it counts them: “Nonstop”, “1 stop · 1h 05m”, “2 stops”. A red-eye that lands the next day takes dayOffset={1} and prints a small +1 after the time. For a round trip, wrap each direction in a FlightCardLeg, as in the card at the top of the page: the legs part with a hairline and share one price.

WNSouthwestWN 880
1:20 PMLAXLos Angeles
2h 20mNonstop
4:40 PMDENDenver
F9FrontierF9 2104 · F9 1187
6:00 AMLAXLos Angeles
4h 40mvia PHX1 stop · 1h 05m
11:40 AMDENDenver
DLDeltaDL 2210 · DL 1408 · DL 902
9:45 PMLAXLos Angeles
8h 45mvia SLCvia MSP2 stops
7:30 AM next dayDENDenver

Results list#

When the assistant finds several, put them in a FlightCardList. They share one frame and part with hairlines. Give the list a value and each card becomes one option of a radio group: click anywhere on a card, or move with the arrow keys. The picked card fills its circle in ink and takes a faint wash, so the choice reads without turning the list into a rainbow.

LAX to DENThu, Jun 18One way1 adult
UAUnitedUA 1532
Best
8:15 AMLAX
2h 35mNonstop
11:50 AMDEN
$218per person
F9FrontierF9 2104
Cheapest
6:00 AMLAX
4h 40mvia PHX1 stop · 1h 05m
11:40 AMDEN
$129per person
WNSouthwestWN 880
Fastest
1:20 PMLAX
2h 20mNonstop
4:40 PMDEN
$236per person
DLDeltaDL 2210
9:45 PMLAX
8h 45mvia SLC1 stop · 3h 10m
7:30 AM next dayDEN
$164per person
UA 1532 · $218

Booked flight and status#

Once it is booked, set status on the root and drop in FlightCardStatus: on time, delayed, boarding, departed, landed or cancelled, worded and toned for you. A delay keeps the scheduled time struck through above the new one, which turns amber; a cancellation strikes both times through. FlightCardDetails holds the booking facts a traveller looks for at the airport.

LHLufthansaLH 456
On time
Fri, Jun 1913:55FRAFrankfurt
11h 40mNonstop
16:35SFOSan Francisco
Terminal
1
Gate
B44
Seat
32K
UAUnitedUA 1532
Boarding
Thu, Jun 188:15 AMLAXLos Angeles
2h 35mNonstop
11:50 AMDENDenver
Terminal
7
Gate
71A
Seat
14C
KLKLMKL 605 · Thu, Jun 18
Delayed
was 10:0510:55AMSAmsterdam
11h 15mNonstop
was 12:2013:10SFOSan Francisco
Terminal
2
Gate
D7
Seat
24A
WNSouthwestWN 880
Cancelled
Sat, Jun 201:20 PMLAXLos Angeles
2h 20mNonstop
4:40 PMDENDenver
Terminal
1
Gate
12
Seat
Open

In a conversation#

The card sits in the assistant's reply at the column's width, under the sentence that explains it. The route is a grid with the path as the flexible column, so the times keep their size and never wrap in a 360px column; the path and the city give way first.

Is my flight to San Francisco still on time?
KL 605 is running 50 minutes late. It now leaves Schiphol at 10:55 and lands in San Francisco at 13:10. Your gate and seat have not changed.
KLKLMKL 605 · Thu, Jun 18
Delayed
was 10:0510:55AMSAmsterdam
11h 15mNonstop
was 12:2013:10SFOSan Francisco
Booking
K7XQ2P
Terminal
2
Gate
D7
Seat
24A

For a search, echo the query back as chips above the list so the traveller can see what was searched before they compare.

Find me a flight from LA to Denver on June 18
Four options on Thursday. United is the best balance of time and price; Frontier is the cheapest if you can take a 6 AM departure.
LAX to DENThu, Jun 18One way1 adult
UAUnitedUA 1532
Best
8:15 AMLAX
2h 35mNonstop
11:50 AMDEN
$218per person
F9FrontierF9 2104
Cheapest
6:00 AMLAX
4h 40mvia PHX1 stop · 1h 05m
11:40 AMDEN
$129per person
WNSouthwestWN 880
Fastest
1:20 PMLAX
2h 20mNonstop
4:40 PMDEN
$236per person
DLDeltaDL 2210
9:45 PMLAX
8h 45mvia SLC1 stop · 3h 10m
7:30 AM next dayDEN
$164per person

Loading#

While results stream in, FlightCardSkeleton holds the shape of a row inside the same list, so the frame and its hairlines are already in place when the flights arrive.

API reference#

FlightCard

One flight or one itinerary, an <article>, and the context every part reads. Every part accepts className, merged last, and forwards its element's props. flightCardVariants is the recipe.

PropTypeDefaultDescription
status"on-time" | "delayed" | "boarding" | "departed" | "landed" | "cancelled"-Where a booked flight stands. Words and tones FlightCardStatus; cancelled strikes the times through. Leave it out for a search result.
valuestring-Inside a selectable FlightCardList, the value this card stands for. The whole card becomes the radio.
disabledboolean-A selectable card that cannot be picked (sold out).
Data attributeDescription
data-slot"flight-card".
data-statusThe status.
data-selectedPresent on the picked card of a selectable list.

FlightCardHeader

The top line, a <div>: the airline first, then whatever follows it pushed to the end (a tag, the status, a price). In a selectable list it ends with the radio circle.

Data attributeDescription
data-slot"flight-card-header"; the circle is "flight-card-control".

FlightCardAirline

Who flies it: a 32px Avatar with the airline's code as the fallback, the name, and the flight number under it.

PropTypeDefaultDescription
name*React.ReactNode-The airline.
codestring-The two-letter IATA code, shown in the mark until a logo loads or when there is none.
srcstring-A logo image. Use your own licensed assets.
flightReact.ReactNode-The second line: flight number, aircraft, cabin.
colorAvatarProps["color"]-Tints the fallback mark.
childrenReact.ReactNode-Replaces the code in the mark, for a glyph.
Data attributeDescription
data-slot"flight-card-airline".

FlightCardTag

Why this result stands out, a small soft Badge. Forwards Badge props.

PropTypeDefaultDescription
kind"best" | "cheapest" | "fastest"-A preset wording and tone.
childrenReact.ReactNode-Your own wording.
Data attributeDescription
data-slot"flight-card-tag".
data-kindThe kind.

FlightCardStatus

A dot Badge worded and toned from the root's status. Forwards Badge props.

PropTypeDefaultDescription
variantBadgeProps["variant"]-Overrides the tone the status picks.
childrenReact.ReactNode-Overrides the wording ("Delayed 50 min").
Data attributeDescription
data-slot"flight-card-status".

FlightCardLeg

One direction of an itinerary, a <section>. Legs that follow each other part with a hairline, so outbound and return read as one card.

PropTypeDefaultDescription
labelReact.ReactNode-The caption above it ("Outbound · Thu, Jun 18").
Data attributeDescription
data-slot"flight-card-leg".

FlightCardRoute

Departure, path and arrival on one shared grid, so the times, the airport codes and the path's caption line up row by row. A <div>.

Data attributeDescription
data-slot"flight-card-route".

FlightCardEndpoint

One end of the route. The first sits on the start edge, the last on the end edge. The time is 18px semibold in tabular-nums and never wraps.

PropTypeDefaultDescription
time*React.ReactNode-The local time, already formatted.
codeReact.ReactNode-The airport code (LAX).
cityReact.ReactNode-The city, truncated under the code.
dateReact.ReactNode-A small date above the time, for itineraries.
wasReact.ReactNode-The scheduled time, struck through above a changed one. The new time turns amber.
dayOffsetnumber-Days after departure: 1 prints +1 and reads "next day".
Data attributeDescription
data-slot"flight-card-endpoint".

FlightCardPath

The line between the two ends, with the duration above and the stops on it. With no stops it reads "Nonstop", with one "1 stop · 1h 05m", with more "2 stops". Stops must be direct children.

PropTypeDefaultDescription
durationReact.ReactNode-Total travel time, above the line.
captionReact.ReactNode-Replaces the stops summary under the line.
nonstopLabelReact.ReactNode"Nonstop"The summary with no stops.
stopsLabel(count: number) => React.ReactNode(count) => count === 1 ? "1 stop" : ${count} stopsThe summary with stops, given their count.
Data attributeDescription
data-slot"flight-card-path"; the line is "flight-card-path-line".

FlightCardStop

A connection, its airport code set on the line. Reads "via PHX" to a screen reader.

PropTypeDefaultDescription
code*React.ReactNode-The airport code.
layoverReact.ReactNode-Time on the ground. With a single stop it joins the summary.
Data attributeDescription
data-slot"flight-card-stop".

FlightCardPrice

The fare, 18px semibold in tabular-nums, with a note under it. Start-aligned in the footer, end-aligned when you place it in the header.

PropTypeDefaultDescription
noteReact.ReactNode-What the fare covers ("Round trip per person", "Economy").
Data attributeDescription
data-slot"flight-card-price".

FlightCardDetails

Booking facts as a <dl> under a hairline, spread across one line and wrapping only when they run out of room. Four facts fit a 360px chat column.

Data attributeDescription
data-slot"flight-card-details".

FlightCardDetail

One fact, a small label over its value.

PropTypeDefaultDescription
label*React.ReactNode-What it is ("Gate").
children*React.ReactNode-The value.
Data attributeDescription
data-slot"flight-card-detail".

FlightCardFooter

The last line, a <div>: the price on the start edge, the actions on the end, wrapping when narrow.

Data attributeDescription
data-slot"flight-card-footer".

FlightCardActions

The buttons, pushed to the end and raised above a selectable card's radio. App UI: size="sm", text only.

Data attributeDescription
data-slot"flight-card-actions".

FlightCardList

Several results in one frame. The cards inside drop their own frame and part with hairlines. Give it value, defaultValue or onValueChange and it becomes a Radix RadioGroup: each card with a value is one option, picked by click or the arrow keys.

PropTypeDefaultDescription
valuestring-Controlled: the picked card.
defaultValuestring-Uncontrolled: the card picked at first.
onValueChange(value: string) => void-Called when a card is picked.
aria-labelstring-Names the group ("Flights from LAX to DEN").
Data attributeDescription
data-slot"flight-card-list".

FlightCardSkeleton

The card's shape while results stream in: the airline, two times and the line between. Hidden from screen readers; joins a list's hairlines.

Data attributeDescription
data-slot"flight-card-skeleton".

FAQ#

From you. FlightCardAirline takes a src for a logo you have the rights to, and shows the airline's two-letter code in the mark until it loads or when there is none. Koala ships no airline branding.