Numbers may dominate most datasets, but tables come alive when they incorporate the full spectrum of data types. Dates tell us when something happened. Text provides names, descriptions, and categorical labels. URLs connect tables to the broader web. Images and icons add visual meaning that words and numbers alone cannot convey. Flags identify countries at a glance. Chemical formulas render with proper subscripts. This chapter explores the formatting functions that handle all of these non-numeric data types, transforming raw values into polished presentations.
The formatters covered here share a common philosophy with the numeric functions from the previous chapter: they operate on the underlying data without modifying it, they support locale-aware rendering where appropriate, and they integrate seamlessly into gt pipelines. But they also introduce capabilities unique to their data types. Date formatters must navigate the labyrinth of international conventions (is January 5th written as 1/5 or 5/1?). Text formatters can render Markdown, generate hyperlinks, or display email addresses. Image formatters embed graphics directly into cells. And special formatters like fmt_flag() and fmt_icon() bridge the gap between data and visual communication.
Consider a table of international sales data. The numeric columns benefit from the fmt_*() functions we’ve already learned. But the country codes: they’re more meaningful as flag icons. Report dates need to follow the conventions of whoever is reading the table. And those product descriptions with markdown formatting should of course render properly rather than showing raw asterisks and brackets. This chapter gives you the tools to handle all such cases.
We’ll start with temporal data (dates, times, and durations) where the challenge lies in choosing among dozens of valid formats for the same underlying value. From there, we move to text and URL formatting, including Markdown rendering. Then we explore the visual formatters that can embed images, country flags, and icons directly into your table cells. Finally, we cover specialized formatters for units, chemical formulas, and country names. By the end of this chapter, you’ll have the complete toolkit for formatting any type of data that might appear in your tables.
4.1 Date, time, and duration formats
Temporal data presents unique formatting challenges. The same date can be expressed in dozens of valid formats, and the choice depends on audience, locale, and context. gt provides fmt_date(), fmt_time(), fmt_datetime(), and fmt_duration() to handle these cases with support for 41 preset date styles and extensive localization.
4.1.1fmt_date()
The fmt_date() function formats date values using one of 41 preset styles. Input can be Date objects, POSIXt datetime objects, or character strings in ISO 8601 format.
The date column now displays in “Month Day, Year” format, a common style in American English contexts.
Different date styles serve different purposes:
exibble|>dplyr::select(date)|>dplyr::slice(1:4)|>gt()|>cols_add( iso =date, full =date, compact =date)|>fmt_date(columns =date, date_style ="wday_month_day_year")|>fmt_date(columns =iso, date_style ="iso")|>fmt_date(columns =full, date_style ="day_month_year")|>fmt_date(columns =compact, date_style ="yMd")|>cols_label( date ="Full (US)", iso ="ISO 8601", full ="Full (UK)", compact ="Compact")
Full (US)
ISO 8601
Full (UK)
Compact
Thursday, January 15, 2015
2015-01-15
15 January 2015
1/15/2015
Sunday, February 15, 2015
2015-02-15
15 February 2015
2/15/2015
Sunday, March 15, 2015
2015-03-15
15 March 2015
3/15/2015
Wednesday, April 15, 2015
2015-04-15
15 April 2015
4/15/2015
This table demonstrates four different date styles from the same source data. The “flexible” styles (like "yMd") automatically adapt to the specified locale.
To explore all 41 available date styles, use info_date_style(), which displays an informative gt table showing each style’s name, a description, and whether it’s locale-flexible:
info_date_style()
For reports that combine data from different regional sources, you can use from_column() to apply different date styles per row:
Each row displays the date in the format conventional for that region: “March 15, 2025” for the US, “15 March 2025” for the UK, and so on. This technique is valuable when creating localized reports or when displaying historical data that was originally recorded in different regional formats.
You can also use from_column() to switch locales, which affects how month and day names are rendered. When combined with flexible date styles (like "yMMMd" or "yMMMEd"), locales also change the ordering of date components to match regional conventions:
Notice that it’s not just translation: the US format puts the month before the day ("Jun 15, 2025"), while European locales put the day first ("15 juin 2025" in French). Asian locales typically use year-month-day order. The flexible date styles automatically adapt to each locale’s conventions for both component ordering and separators.
4.1.2fmt_time()
Time formatting follows similar principles. The fmt_time() function formats input values to time values using one of 25 preset time styles. Input can be in the form of POSIXt (i.e., datetimes), character (must be in the ISO 8601 forms of HH:MM:SS or YYYY-MM-DD HH:MM:SS), or Date (which always results in the formatting of 00:00:00).
The times now display in 12-hour format with AM/PM indicators, a common format for general audiences.
To see all available time styles, use info_time_style():
info_time_style()
Like date styles, some time styles are locale-flexible (adapting to 12-hour or 24-hour conventions based on the locale), while others produce fixed output regardless of locale.
4.1.3fmt_datetime()
When you have full datetime values, fmt_datetime() combines date and time formatting. This function offers two approaches: using preset styles for the date and time components separately, or using a custom format string for complete control over the output.
The datetime column now shows both the date (in “Feb 29, 2000” style) and time (in 24-hour format), providing complete temporal information. The sep argument controls the separator between date and time (defaults to a single space).
4.1.3.1 Custom formatting with the format argument
For complete control over datetime output, the format argument accepts formatting strings in two different syntaxes: CLDR (Common Locale Data Repository) datetime patterns and strptime format codes. Both are powerful, but CLDR patterns offer better locale support and more formatting options.
CLDR datetime patterns use pattern characters that repeat to indicate output width. Here are some examples using a datetime of "2018-07-04T22:05:09":
"EEEE, MMMM d, y" → "Wednesday, July 4, 2018"
"MMM d, y 'at' h:mm a" → "Jul 4, 2018 at 10:05 PM"
"y-MM-dd HH:mm" → "2018-07-04 22:05"
The key CLDR pattern characters include:
Character
Meaning
Examples
y
Year
y → “2018”, yy → “18”
M
Month
M → “7”, MM → “07”, MMM → “Jul”, MMMM → “July”
d
Day of month
d → “4”, dd → “04”
E
Day of week
E → “Wed”, EEEE → “Wednesday”
H
Hour (0-23)
H → “22”, HH → “22”
h
Hour (1-12)
h → “10”, hh → “10”
m
Minute
m → “5”, mm → “05”
s
Second
s → “9”, ss → “09”
a
AM/PM
a → “PM”
Literal text can be included by surrounding it with single quotes:
exibble|>dplyr::select(datetime)|>dplyr::slice(1:4)|>gt()|>fmt_datetime( columns =datetime, format ="EEEE, MMMM d, y 'at' h:mm a")
datetime
Monday, January 1, 2018 at 2:22 AM
Friday, February 2, 2018 at 2:33 PM
Saturday, March 3, 2018 at 3:44 AM
Wednesday, April 4, 2018 at 3:55 PM
strptime format codes use a % prefix for each component. The same datetime formatted with strptime codes:
"%A, %B %e, %Y" → "Wednesday, July 4, 2018"
"%b %e, %Y at %I:%M %p" → "Jul 4, 2018 at 10:05 PM"
"%Y-%m-%d %H:%M" → "2018-07-04 22:05"
Common strptime codes include:
Code
Meaning
Example
%Y
4-digit year
"2018"
%y
2-digit year
"18"
%m
Month number (zero-padded)
"07"
%b
Abbreviated month name
"Jul"
%B
Full month name
"July"
%d
Day (zero-padded)
"04"
%e
Day (space-padded)
" 4"
%A
Full weekday name
"Wednesday"
%H
Hour 0-23
"22"
%I
Hour 1-12
"10"
%M
Minute
"05"
%S
Second
"09"
%p
AM/PM
"PM"
Here’s the same friendly datetime format from before, now using strptime codes instead of CLDR patterns:
exibble|>dplyr::select(datetime)|>dplyr::slice(1:4)|>gt()|>fmt_datetime( columns =datetime, format ="%A, %B %e, %Y at %I:%M %p")
datetime
Monday, January 1, 2018 at 02:22 AM
Friday, February 2, 2018 at 02:33 PM
Saturday, March 3, 2018 at 03:44 AM
Wednesday, April 4, 2018 at 03:55 PM
The output is nearly identical to the CLDR version. The "%A" gives the full weekday name, "%B" the full month name, "%e" the day without zero-padding, and "%I:%M %p" produces 12-hour time with AM/PM.
4.1.3.2 Working with time zones
The tz argument lets you convert datetimes to a specific time zone for display. This is particularly useful when your data is stored in UTC but you want to display it in local time:
exibble|>dplyr::select(datetime)|>dplyr::slice(1:3)|>gt()|>fmt_datetime( columns =datetime, format ="EEEE, MMMM d, y 'at' h:mm a (zzzz)", tz ="America/Vancouver")
datetime
Monday, January 1, 2018 at 2:22 AM (Pacific Standard Time)
Friday, February 2, 2018 at 2:33 PM (Pacific Standard Time)
Saturday, March 3, 2018 at 3:44 AM (Pacific Standard Time)
The "zzzz" pattern character displays the full time zone name. You can use shorter variants like "z" (abbreviated) or "Z" (UTC offset).
4.1.3.3 CLDR vs strptime: which to choose?
CLDR patterns are generally preferred because they have:
better locale support, where patterns adapt to locale conventions automatically
more options: patterns for eras, quarters, flexible day periods (“in the afternoon”), and more
richer time zone display options
Use strptime when you need compatibility with R’s base date formatting functions or when working with existing format strings from other code.
For a comprehensive reference of all CLDR pattern fields and strptime format codes, see Appendix B.
4.1.4fmt_duration()
For time intervals and durations, fmt_duration() converts numeric values (representing seconds or other units) into human-readable duration strings.
Each style serves different purposes: "narrow" is compact, "wide" is more readable, "colon-sep" follows familiar clock notation (HH:MM:SS), and "iso" produces ISO 8601 duration format.
4.2 Formatting with Markdown, creating links, adding images
Beyond numeric and temporal data, gt can format text as Markdown, create hyperlinks, and embed images directly in table cells.
4.2.1fmt_markdown()
The fmt_markdown() function interprets cell content as Markdown, rendering formatting, links, and other Markdown elements.
The email addresses become clickable links that will open the user’s email client when clicked. This is particularly useful for contact directories or team rosters.
Here’s a more complete example using the peeps dataset to create a contact directory with formatted email addresses and mailing addresses:
This example demonstrates how fmt_email() integrates with other gt functions to create a polished contact directory. The email addresses are clickable, while cols_merge() combines address components into a single formatted cell.
4.2.4fmt_image()
The fmt_image() function renders file paths as inline images.
This function is particularly powerful for creating visual catalogs or including small graphics within data tables.
4.3 Flags and country formatting
When presenting international data, flags and country names can provide immediate visual recognition and context.
4.3.1fmt_flag()
The fmt_flag() function converts 2- or 3-letter ISO 3166-1 country codes into circular flag icons. The function seamlessly handles both uppercase and lowercase codes.
countrypops|>dplyr::filter(year==2021)|>dplyr::slice_max(population, n =8)|>dplyr::select(country_code_2, country_name, population)|>gt()|>fmt_flag(columns =country_code_2)|>fmt_integer(columns =population)|>cols_label( country_code_2 ="", country_name ="Country", population ="Population (2021)")
Country
Population (2021)
India
1,414,203,896
China
1,412,360,000
United States
332,099,760
Indonesia
276,758,053
Pakistan
239,477,801
Nigeria
218,529,286
Brazil
209,550,294
Bangladesh
167,658,854
The two-letter country codes are replaced with their corresponding flag icons. Hovering over a flag shows the country name as a tooltip (controlled by use_title = TRUE, the default). This adds visual interest and makes it easier to quickly identify countries.
4.3.1.1 Controlling flag size
The height argument adjusts the size of flag icons. The default "1em" scales with the text size, but you can specify other CSS units:
dplyr::tibble( country =c("US", "CN", "IN", "BR"), small =country, medium =country, large =country)|>gt()|>fmt_flag(columns =small, height ="0.8em")|>fmt_flag(columns =medium, height ="1.5em")|>fmt_flag(columns =large, height ="2.5em")|>cols_label( country ="Code", small ="Small", medium ="Medium", large ="Large")|>cols_align(align ="center", columns =c(small, medium, large))
Code
Small
Medium
Large
US
CN
IN
BR
Different flag sizes can help establish visual hierarchy. Larger flags might be appropriate in header sections or key summary rows, while smaller flags work well in dense data tables.
4.3.1.2 Multiple flags per cell
The fmt_flag() function works well even when there are multiple country codes within the same cell. It parses comma-separated codes automatically, and when rendered to HTML, hovering over each flag icon displays a tooltip with the country name. The input must use commas as delimiters (with no spaces), while the sep argument controls the spacing between rendered flag icons in the output.
dplyr::tibble( region =c("North America", "European Union (founders)", "BRICS", "Nordic Countries"), countries =c("US,CA,MX", "BE,FR,DE,IT,LU,NL", "BR,RU,IN,CN,ZA", "DK,FI,IS,NO,SE"))|>gt()|>fmt_flag(columns =countries, sep =" ")|>cols_label( region ="Region/Group", countries ="Member Countries")|>cols_width(countries~px(300))
Region/Group
Member Countries
North America
European Union (founders)
BRICS
Nordic Countries
Multiple flags are displayed inline, making it easy to see country groupings or collaborations at a glance. The default sep = " " provides a single space between flags, but you can adjust this for tighter or looser spacing.
Here’s another example that groups countries by population size, demonstrating how fmt_flag() handles dynamically aggregated country codes:
countrypops|>dplyr::filter(year==2021, population<100000)|>dplyr::select(country_code_2, population)|>dplyr::mutate(population_class =cut(population, breaks =scales::breaks_pretty(n =5)(population)))|>dplyr::group_by(population_class)|>dplyr::summarize( countries =paste0(country_code_2, collapse =","))|>dplyr::arrange(desc(population_class))|>gt()|>tab_header(title ="Countries with Small Populations")|>fmt_flag(columns =countries)|>fmt_bins( columns =population_class, fmt =~fmt_integer(., suffixing =TRUE))|>cols_label( population_class ="Population Range", countries ="Countries")|>cols_width(population_class~px(150))
Countries with Small Populations
Population Range
Countries
80K–100K
60K–80K
40K–60K
20K–40K
0–20K
This example uses paste0() with collapse = "," to aggregate country codes within each population bin, and fmt_flag() seamlessly renders all flags in each cell.
4.3.1.3 Localized tooltips
The locale argument controls the language used in the hover tooltips. This is especially useful when creating tables for international audiences:
When hovering over these flags, the country names appear in the specified language. The English column shows “Japan”, the Japanese column shows “日本”, and the Korean column shows “일본”.
4.3.1.4 Merging flags into row labels
Flag icons can be merged into the stub column to create visually distinctive row labels:
The stub now displays flag icons followed by country names, creating an elegant visual identifier for each row. This technique is particularly effective for country comparisons or regional analyses.
4.3.2fmt_country()
Conversely, if you have country codes but want to display full country names, use fmt_country(). This function accepts both 2-letter and 3-letter ISO 3166-1 country codes and converts them to full country names from the Unicode CLDR (Common Locale Data Repository).
countrypops|>dplyr::filter(year==2021)|>dplyr::slice_max(population, n =5)|>dplyr::select(country_code_3, population)|>gt()|>fmt_country(columns =country_code_3)|>fmt_integer(columns =population)|>cols_label( country_code_3 ="Country", population ="Population (2021)")
Country
Population (2021)
India
1,414,203,896
China
1,412,360,000
United States
332,099,760
Indonesia
276,758,053
Pakistan
239,477,801
The three-letter country codes are replaced with full country names.
The country names come from the Unicode CLDR (Common Locale Data Repository), where names are agreed upon by consensus. Furthermore, these names can be localized to any of 574 different locales via the locale argument.
Here’s an example showing how the same country code resolves to different exonyms depending on the locale:
dplyr::tibble( locale =c("en", "fr", "de", "it", "ja", "zh", "ko", "ar"), country ="DE")|>gt()|>fmt_country(columns =country, locale =from_column("locale"))|>cols_label( locale ="Locale", country ="Country Name")
Locale
Country Name
en
Germany
fr
Allemagne
de
Deutschland
it
Germania
ja
ドイツ
zh
德国
ko
독일
ar
ألمانيا
Each row shows “DE” (Germany) translated into the language specified by the locale column: English, French, German, Japanese, Chinese, Arabic, and Russian.
Here’s a more comprehensive example using the films dataset. The fmt_country() function handles multiple comma-separated country codes per cell, which is common for international co-productions:
films|>dplyr::filter(year==1959)|>dplyr::select(title, run_time, director, countries_of_origin, imdb_url)|>gt()|>tab_header(title ="Feature Films in Competition at the 1959 Festival")|>fmt_country(columns =countries_of_origin, sep =", ")|>fmt_url( columns =imdb_url, label =fontawesome::fa("imdb", fill ="black"))|>cols_merge( columns =c(title, imdb_url), pattern ="{1} {2}")|>cols_label( title ="Film", run_time ="Length", director ="Director", countries_of_origin ="Country")|>opt_vertical_padding(scale =0.5)|>opt_horizontal_padding(scale =2.5)|>opt_table_font(stack ="classical-humanist", weight ="bold")|>opt_stylize(style =1, color ="gray")|>tab_options(heading.title.font.size =px(26))
Feature Films in Competition at the 1959 Festival
Film
Length
Director
Country
Araya
1h 30m
Margot Benacerraf
Venezuela, France
Compulsion
1h 43m
Richard Fleischer
United States
Eva
1h 32m
Rolf Thiele
Austria
Fanfare
1h 26m
Bert Haanstra
Netherlands
Miss April
1h 38m
Göran Gentele
Sweden
Arms and the Man
1h 40m
Franz Peter Wirth
Germany
Hiroshima mon amour
1h 30m
Alain Resnais
France, Japan
Court Martial
1h 24m
Kurt Meisel
Germany
The Soldiers of Pancho Villa
1h 37m
Ismael Rodríguez
Mexico
Lajwanti
2h
Narendra Suri
India
The 400 Blows
1h 39m
François Truffaut
France
Honeymoon
1h 49m
Michael Powell
United Kingdom, Spain
Bloody Twilight
1h 28m
Andreas Labrinos
Greece
Middle of the Night
1h 58m
Delbert Mann
United States
Nazarín
1h 34m
Luis Buñuel
Mexico
Black Orpheus
1h 40m
Marcel Camus
Brazil, France, Italy
A Home for Tanya
1h 40m
Lev Kulidzhanov
USSR
Policarpo
1h 44m
Mario Soldati
Italy, France, Spain
Portuguese Rhapsody
1h 26m
João Mendes
Portugal
Room at the Top
1h 57m
Jack Clayton
United Kingdom
A Midsummer Night's Dream
1h 16m
Jirí Trnka
Czechoslovakia
The Snowy Heron
1h 37m
Teinosuke Kinugasa
Japan
Stars
1h 31m
Konrad Wolf
East Germany, Bulgaria
The Sinner
1h 30m
Shen Tien
Taiwan
The Diary of Anne Frank
3h
George Stevens
United States
Desire
1h 35m
Vojtech Jasný
Czechoslovakia
Train Without a Timetable
2h 1m
Veljko Bulajic
Yugoslavia
Sugar Harvest
1h 17m
Lucas Demare
Argentina
Édes Anna
1h 24m
Zoltán Fábri
Hungary
Notice that fmt_country() can resolve historical country codes that no longer exist. Codes like "SU" (USSR), "CS" (Czechoslovakia), and "YU" (Yugoslavia) are properly resolved to their historical names, which is essential when working with archival data.
4.3.2.1 Localized country names
The locale argument enables country names to be displayed in different languages, making tables more accessible to international audiences:
dplyr::tibble( code =c("JP", "DE", "BR", "IN", "ZA"), english =code, japanese =code, german =code)|>gt()|>fmt_country(columns =english, locale ="en")|>fmt_country(columns =japanese, locale ="ja")|>fmt_country(columns =german, locale ="de")|>cols_label( code ="Code", english ="English", japanese ="日本語", german ="Deutsch")
Code
English
日本語
Deutsch
JP
Japan
日本
Japan
DE
Germany
ドイツ
Deutschland
BR
Brazil
ブラジル
Brasilien
IN
India
インド
Indien
ZA
South Africa
南アフリカ
Südafrika
The same country codes are rendered in English, Japanese, and German. This is particularly useful for creating multilingual reports or tables intended for specific regional audiences.
4.3.2.2 Multiple countries per cell
When cells contain multiple comma-separated country codes, fmt_country() handles them automatically. The sep argument controls the separator between country names:
dplyr::tibble( film =c("The Grand Budapest Hotel", "Amélie", "Run Lola Run"), countries =c("US,DE", "FR", "DE"))|>gt()|>fmt_country(columns =countries, sep =" / ")|>cols_label( film ="Film", countries ="Production Countries")
Film
Production Countries
The Grand Budapest Hotel
United States / Germany
Amélie
France
Run Lola Run
Germany
For films with co-production arrangements, multiple countries are displayed with a custom separator (here, ” / “), making the relationships clear while maintaining readability.
4.3.2.3 Combining fmt_flag() and fmt_country()
Flag icons and country names work beautifully together. Here’s how to combine them using cols_merge():
countrypops|>dplyr::filter(year==2021)|>dplyr::slice_max(population, n =8)|>dplyr::select(country_code_2, country_code_3, population)|>gt()|>fmt_flag(columns =country_code_2)|>fmt_country(columns =country_code_3)|>cols_merge( columns =c(country_code_2, country_code_3), pattern ="{1} {2}")|>fmt_integer(columns =population)|>cols_label( country_code_2 ="Country", population ="Population (2021)")
Country
Population (2021)
India
1,414,203,896
China
1,412,360,000
United States
332,099,760
Indonesia
276,758,053
Pakistan
239,477,801
Nigeria
218,529,286
Brazil
209,550,294
Bangladesh
167,658,854
The merged column now displays both the flag icon and the country name side by side, creating a visually rich and informative presentation. The flag provides instant visual recognition while the name ensures clarity.
4.4 Icons in table cells
4.4.1fmt_icon()
For adding Font Awesome icons to table cells, fmt_icon() converts icon names to rendered icons.
The icon names are replaced with the corresponding Font Awesome icons. This is useful for creating visual category indicators or status displays.
You can customize icon appearance with color options:
dplyr::tibble( status =c("Success", "Warning", "Error", "Info"), icon =c("check-circle", "exclamation-triangle", "times-circle", "info-circle"), color =c("green", "orange", "red", "blue"))|>gt()|>fmt_icon(columns =icon, fill_color =from_column("color"))|>cols_hide(columns =color)
status
icon
Success
Warning
Error
Info
Each icon is colored according to its status, creating an immediately recognizable visual language for status indicators.
To explore all available icons, use info_icons():
info_icons()
4.5 Scientific and technical notation
4.5.1fmt_units()
Scientific and technical writing often requires properly formatted units with superscripts, subscripts, and special symbols. The fmt_units() function interprets a simple text notation and renders it with correct typography.
Note
The units notation described here is the same notation used in column labels (see the section on Incorporating units with gt’s units notation in Chapter 2). That section provides additional details on Greek letters, automatic symbol conversions, chemical formulas, and text formatting options.
The unit strings are rendered with proper formatting: exponents become superscripts, the asterisk becomes a multiplication dot, and the slash indicates division. This notation follows the conventions of scientific typesetting without requiring HTML or LaTeX markup in your data.
The units notation uses a simple but expressive syntax. Here are the key elements:
Notation
Meaning
Example Input
Renders As
^
Superscript (exponent)
m^2
m²
_
Subscript
x_0
x₀
*
Multiplication (·)
kg*m
kg·m
/
Division (per)
m/s
m/s
()
Grouping
J/(kg*K)
J/(kg·K)
{{}}
Subscript text
x_{{avg}}
xavg
-
Minus in exponent
m^-1
m⁻¹
These elements can be combined to express complex units:
The notation is intuitive for anyone familiar with scientific writing. You write units almost as you would speak them. So “meters per second squared” becomes m/s^2, and “watts per square meter kelvin” becomes W/(m^2*K).
The unit notation supports a rich syntax for complex expressions:
Parentheses group terms correctly, ensuring the denominator is rendered as a single unit. This creates publication-ready unit formatting directly from simple text notation.
4.5.2fmt_chem()
Chemical formulas require specific formatting: subscripts for atom counts, superscripts for charges, and proper handling of isotopes and reactions. The fmt_chem() function interprets chemistry notation and renders it correctly.
The chemistry notation that gt uses should be intuitive for anyone familiar with chemical formulas. Here’s a comprehensive reference:
Formatting with fmt_chem()
CHEMICAL FORMULAS
CH3O2
CH3O2
(NH4)2S
(NH4)2S
Ca3(PO4)2
Ca3(PO4)2
Sb2O3
Sb2O3
CHARGES
H+
H+
[AgCl2]-
[AgCl2]−
CrO4^2-
CrO42−
CO3^2-
CO32−
Y^99+
Y99+
STOICHIOMETRY
0.5 H2O
0.5 H2O
2H2O2
2 H2O2
2 H2O
2 H2O
NUCLIDES/ISOTOPES
^{230}_{90}Th+
230 90Th+
^65Cu^{2+}
65 Cu2+
^27_14Si13
27 14Si13
^{0}_{-1}n^{-}
0 -1n−
^0_-1n-
0 -1n−
VARIABLES ITALICIZED
NO_x
NOx
Fe^n+
Fen+
x Na(NH4)HPO4
x Na(NH4)HPO4
*n* H2O
n H2O
*n*-C5H12
n−C5H12
GREEK CHARACTERS
:delta: ^13C
δ13 C
:mu:-Cl
µ−Cl
PRECIPITATES AND GASES
SO4^2- + Ba^2+ -> BaSO4 v
SO42− + Ba2+ BaSO4↓
A v B (v) -> B ^ B (^)
A↓ B↓ B↑ B↑
ADDITION COMPOUNDS
KCr(SO4)2 * 12 H2O
KCr(SO4)2⋅12 H2O
KCl . MgCl2 . 6 H2O
KCl⋅MgCl2⋅6 H2O
CHEMICAL REACTIONS
2CH3OH -> CH3OCH3 + H2O
2 CH3OH CH3OCH3 + H2O
O3 -> O(^1D) + O2
O3 O(1 D) + O2
H2(g) + I2(g) <=> 2HI (g)
H2(g) + I2(g) 2 HI (g)
BONDS
C6H5-CHO
C6H5−CHO
CH3CH=CH2
CH3CH=CH2
Let’s see basic formula formatting in action:
dplyr::tibble( name =c("Water", "Sulfuric acid", "Glucose", "Calcium carbonate"), formula =c("H2O", "H2SO4", "C6H12O6", "CaCO3"))|>gt()|>fmt_chem(columns =formula)
name
formula
Water
H2O
Sulfuric acid
H2SO4
Glucose
C6H12O6
Calcium carbonate
CaCO3
The numbers in the formulas become subscripts, transforming "H2O" into the properly typeset "H₂O". This is essential for any table presenting chemical data.
Ionic compounds and charges are handled elegantly:
dplyr::tibble( ion =c("Hydroxide", "Sulfate", "Ammonium", "Phosphate", "Iron(III)"), formula =c("OH^-", "SO4^2-", "NH4^+", "PO4^3-", "Fe^3+"))|>gt()|>fmt_chem(columns =formula)
ion
formula
Hydroxide
OH−
Sulfate
SO42−
Ammonium
NH4+
Phosphate
PO43−
Iron(III)
Fe3+
Charges are rendered as superscripts with the appropriate sign, creating properly formatted ionic formulas suitable for scientific publication.
For isotope notation (common in nuclear chemistry and radiochemistry) the function supports full nuclide representation:
The mass number appears as a superscript and the atomic number as a subscript, positioned before the element symbol: the standard convention for nuclide notation.
Chemical reactions can be expressed with various arrow styles:
Each arrow type has a specific meaning in chemistry: -> for irreversible reactions, <--> for reversible reactions, <=> for equilibrium, and <=>> or <<=> for equilibria that favor one direction.
Hydrates and addition compounds use the centered dot notation:
dplyr::tibble( name =c("Copper(II) sulfate pentahydrate","Magnesium sulfate heptahydrate","Chrome alum"), formula =c("CuSO4 . 5 H2O","MgSO4 . 7 H2O","KCr(SO4)2 . 12 H2O"))|>gt()|>fmt_chem(columns =formula)
name
formula
Copper(II) sulfate pentahydrate
CuSO4⋅5 H2O
Magnesium sulfate heptahydrate
MgSO4⋅7 H2O
Chrome alum
KCr(SO4)2⋅12 H2O
The period surrounded by spaces becomes a centered dot (·), the standard notation for waters of hydration and other addition compounds.
4.6 Summary
This chapter has explored the formatting functions that handle non-numeric data: the dates, times, text, URLs, images, and special elements that bring tables to life beyond raw numbers.
The key capabilities we’ve covered:
temporal formatting: fmt_date(), fmt_time(), and fmt_datetime() provide 41 date styles and 25 time styles, with full locale support for international audiences. Custom formatting through CLDR patterns or strptime codes gives you complete control when preset styles aren’t enough. The fmt_duration() function transforms raw seconds into human-readable time spans.
text and links: fmt_markdown() renders rich text formatting within cells, while fmt_url() and fmt_email() create clickable links that connect your tables to external resources.
visual elements: fmt_image() embeds graphics directly in cells, fmt_flag() converts country codes to instantly recognizable flag icons, and fmt_icon() adds Font Awesome icons for status indicators and categorical markers.
scientific notation: fmt_units() renders physical units with proper superscripts and subscripts, while fmt_chem() handles chemical formulas, isotopes, and reaction equations with publication-ready typography.
country handling: fmt_flag() and fmt_country() work together to present international data with visual clarity, supporting localized country names and tooltips.
Together with the numeric formatters from the previous chapter, you now have a complete toolkit for transforming raw data values into polished, meaningful presentations. But formatting is just the first stage of gt’s rendering pipeline.
The next chapter introduces substitution and text transformation functions. These operate after formatting, allowing you to replace specific values (like missing data or zeros) with alternative text, and to transform the final string representation of cell values. This three-stage pipeline (format, substitute, transform) gives you precise control over exactly how every value appears in your finished table.