Rendering Images in the Body
GT.fmt_image() is the go-to tool for rendering images within the body of a table. Below, we’ll present four examples corresponding to the cases outlined in the documentation:
- Case 1: Local file paths.
- Case 2: Full HTTP/HTTPS URLs.
- Case 3: Image names with the
path= argument.
- Case 4: Image names using both the
path= and file_pattern= arguments.
- Case 1 and Case 2 work best for data sourced directly from a database.
- Case 3 is ideal for users dealing with image names relative to a base directory or URL (e.g.,
/path/to/images).
- Case 4 is tailored for users working with patterned image names (e.g.,
metro_{}.svg).
Preparations
For this demonstration, we’ll use the first five rows of the built-in metro dataset, specifically the name and lines columns.
To ensure a smooth walkthrough, we’ll manipulate the data (a Python dictionary) directly. However, in real-world applications, such operations are more likely performed at the DataFrame level to leverage the benefits of vectorized operations.
Show the Code
import pandas as pd
from great_tables import GT, vals, html
from importlib_resources import files
pd.set_option('display.max_colwidth', 150)
data = {
"name": [
"Argentine",
"Bastille",
"Bérault",
"Champs-Élysées—Clemenceau",
"Charles de Gaulle—Étoile",
],
"lines": ["1", "1, 5, 8", "1", "1, 13", "1, 2, 6"],
}
print("""\
data = {
"name": [
"Argentine",
"Bastille",
"Bérault",
"Champs-Élysées—Clemenceau",
"Charles de Gaulle—Étoile",
],
"lines": ["1", "1, 5, 8", "1", "1, 13", "1, 2, 6"],
}\
""")
data = {
"name": [
"Argentine",
"Bastille",
"Bérault",
"Champs-Élysées—Clemenceau",
"Charles de Gaulle—Étoile",
],
"lines": ["1", "1, 5, 8", "1", "1, 13", "1, 2, 6"],
}
Attentive readers may have noticed that the values for the key lines are lists of strings, each containing one or more numbers separated by commas. GT.fmt_image() is specifically designed to handle such cases, allowing users to render multiple images in a single row.
Case 1: Local File Paths
Case 1 demonstrates how to simulate a column containing strings representing local file paths. We’ll use images stored in the data/metro_images directory of Great Tables:
1img_local_paths = files("great_tables") / "data/metro_images"
- 1
-
These image files follow a patterned naming convention, such as
metro_1.svg, metro_2.svg, and so on.
Below is a Pandas DataFrame called metro_mini1, where the case1 column contains local file paths that we want to render as images.
Show the Code
metro_mini1 = pd.DataFrame(
{
**data,
"case1": [
", ".join(
str((img_local_paths / f"metro_{item}").with_suffix(".svg"))
for item in row.split(", ")
)
for row in data["lines"]
],
}
)
metro_mini1
|
name |
lines |
case1 |
| 0 |
Argentine |
1 |
/home/runner/work/great-tables/great-tables/great_tables/data/metro_images/metro_1.svg |
| 1 |
Bastille |
1, 5, 8 |
/home/runner/work/great-tables/great-tables/great_tables/data/metro_images/metro_1.svg, /home/runner/work/great-tables/great-tables/great_tables/d... |
| 2 |
Bérault |
1 |
/home/runner/work/great-tables/great-tables/great_tables/data/metro_images/metro_1.svg |
| 3 |
Champs-Élysées—Clemenceau |
1, 13 |
/home/runner/work/great-tables/great-tables/great_tables/data/metro_images/metro_1.svg, /home/runner/work/great-tables/great-tables/great_tables/d... |
| 4 |
Charles de Gaulle—Étoile |
1, 2, 6 |
/home/runner/work/great-tables/great-tables/great_tables/data/metro_images/metro_1.svg, /home/runner/work/great-tables/great-tables/great_tables/d... |
Local file paths can vary depending on the operating system, which makes it easy to accidentally construct invalid paths. A good practice to mitigate this is to use Python’s built-in pathlib module to construct paths first and then convert them to strings. In this example, img_local_paths is actually an instance of pathlib.Path.
from pathlib import Path
isinstance(img_local_paths, Path) # True
The case1 column is quite lengthy due to the inclusion of img_local_paths. In Case 3, we’ll share a useful trick to avoid repeating the directory name each time—stay tuned!
For now, let’s use GT.fmt_image() to render images by passing "case1" as the first argument:
GT(metro_mini1).fmt_image("case1").cols_align(align="right", columns="case1")
| name |
lines |
case1 |
| Argentine |
1 |
 |
| Bastille |
1, 5, 8 |
 |
| Bérault |
1 |
 |
| Champs-Élysées—Clemenceau |
1, 13 |
 |
| Charles de Gaulle—Étoile |
1, 2, 6 |
 |
Case 2: Full HTTP/HTTPS URLs
Case 2 demonstrates how to simulate a column containing strings representing HTTP/HTTPS URLs. We’ll use the same images as in Case 1, but this time, retrieve them from the Great Tables GitHub repository:
img_url_paths = "https://raw.githubusercontent.com/posit-dev/great-tables/refs/heads/main/great_tables/data/metro_images"
Below is a Pandas DataFrame called metro_mini2, where the case2 column contains full HTTP/HTTPS URLs that we aim to render as images.
Show the Code
metro_mini2 = pd.DataFrame(
{
**data,
"case2": [
", ".join(f"{img_url_paths}/metro_{item}.svg" for item in row.split(", "))
for row in data["lines"]
],
}
)
metro_mini2
|
name |
lines |
case2 |
| 0 |
Argentine |
1 |
https://raw.githubusercontent.com/posit-dev/great-tables/refs/heads/main/great_tables/data/metro_images/metro_1.svg |
| 1 |
Bastille |
1, 5, 8 |
https://raw.githubusercontent.com/posit-dev/great-tables/refs/heads/main/great_tables/data/metro_images/metro_1.svg, https://raw.githubusercontent... |
| 2 |
Bérault |
1 |
https://raw.githubusercontent.com/posit-dev/great-tables/refs/heads/main/great_tables/data/metro_images/metro_1.svg |
| 3 |
Champs-Élysées—Clemenceau |
1, 13 |
https://raw.githubusercontent.com/posit-dev/great-tables/refs/heads/main/great_tables/data/metro_images/metro_1.svg, https://raw.githubusercontent... |
| 4 |
Charles de Gaulle—Étoile |
1, 2, 6 |
https://raw.githubusercontent.com/posit-dev/great-tables/refs/heads/main/great_tables/data/metro_images/metro_1.svg, https://raw.githubusercontent... |
The lengthy case2 column issue can also be addressed using the trick shared in Case 3.
Similarly, we can use GT.fmt_image() to render images by passing "case2" as the first argument:
GT(metro_mini2).fmt_image("case2").cols_align(align="right", columns="case2")
| name |
lines |
case2 |
| Argentine |
1 |
 |
| Bastille |
1, 5, 8 |
 |
| Bérault |
1 |
 |
| Champs-Élysées—Clemenceau |
1, 13 |
 |
| Charles de Gaulle—Étoile |
1, 2, 6 |
 |
Case 3: Image Names with the path= Argument
Case 3 demonstrates how to use the path= argument to specify images relative to a base directory or URL. This approach eliminates much of the repetition in file names, offering a solution to the issues in Case 1 and Case 2.
Below is a Pandas DataFrame called metro_mini3, where the case3 column contains file names that we aim to render as images.
Show the Code
metro_mini3 = pd.DataFrame(
{
**data,
"case3": [
", ".join(f"metro_{item}.svg" for item in row.split(", ")) for row in data["lines"]
],
}
)
metro_mini3
|
name |
lines |
case3 |
| 0 |
Argentine |
1 |
metro_1.svg |
| 1 |
Bastille |
1, 5, 8 |
metro_1.svg, metro_5.svg, metro_8.svg |
| 2 |
Bérault |
1 |
metro_1.svg |
| 3 |
Champs-Élysées—Clemenceau |
1, 13 |
metro_1.svg, metro_13.svg |
| 4 |
Charles de Gaulle—Étoile |
1, 2, 6 |
metro_1.svg, metro_2.svg, metro_6.svg |
Now we can use GT.fmt_image() to render the images by passing "case3" as the first argument and specifying either img_local_paths or img_url_paths as the path= argument:
# equivalent to `Case 1`
(
GT(metro_mini3)
.fmt_image("case3", path=img_local_paths)
.cols_align(align="right", columns="case3")
)
# equivalent to `Case 2`
(
GT(metro_mini3)
.fmt_image("case3", path=img_url_paths)
.cols_align(align="right", columns="case3")
)
| name |
lines |
case3 |
| Argentine |
1 |
 |
| Bastille |
1, 5, 8 |
 |
| Bérault |
1 |
 |
| Champs-Élysées—Clemenceau |
1, 13 |
 |
| Charles de Gaulle—Étoile |
1, 2, 6 |
 |
After exploring Case 1 and Case 2, you’ll likely appreciate the functionality of the path= argument. However, manually constructing file names can still be a bit tedious. If your file names follow a consistent pattern, the file_pattern= argument can simplify the process. Let’s see how this works in Case 4 below.
Case 4: Image Names Using Both the path= and file_pattern= Arguments
Case 4 demonstrates how to use path= and file_pattern= to specify images with names following a common pattern. For example, you could use file_pattern="metro_{}.svg" to reference images like metro_1.svg, metro_2.svg, and so on.
Below is a Pandas DataFrame called metro_mini4, where the case4 column contains a copy of data["lines"], which we aim to render as images.
Show the Code
metro_mini4 = pd.DataFrame({**data, "case4": data["lines"]})
metro_mini4
|
name |
lines |
case4 |
| 0 |
Argentine |
1 |
1 |
| 1 |
Bastille |
1, 5, 8 |
1, 5, 8 |
| 2 |
Bérault |
1 |
1 |
| 3 |
Champs-Élysées—Clemenceau |
1, 13 |
1, 13 |
| 4 |
Charles de Gaulle—Étoile |
1, 2, 6 |
1, 2, 6 |
First, define a string pattern to illustrate the file naming convention, using {} to indicate the variable portion:
file_pattern = "metro_{}.svg"
Next, pass "case4" as the first argument, along with img_local_paths or img_url_paths as the path= argument, and file_pattern as the file_pattern= argument. This allows GT.fmt_image() to render the images:
# equivalent to `Case 1`
(
GT(metro_mini4)
.fmt_image("case4", path=img_local_paths, file_pattern=file_pattern)
.cols_align(align="right", columns="case4")
)
# equivalent to `Case 2`
(
GT(metro_mini4)
.fmt_image("case4", path=img_url_paths, file_pattern=file_pattern)
.cols_align(align="right", columns="case4")
)
| name |
lines |
case4 |
| Argentine |
1 |
 |
| Bastille |
1, 5, 8 |
 |
| Bérault |
1 |
 |
| Champs-Élysées—Clemenceau |
1, 13 |
 |
| Charles de Gaulle—Étoile |
1, 2, 6 |
 |
The file_pattern= argument is typically used in conjunction with the path= argument, but this is not a strict rule. If your local file paths or HTTP/HTTPS URLs follow a pattern, you can use file_pattern= alone without path=. This allows you to include the shared portion of the file paths or URLs directly in file_pattern, as shown below:
file_pattern = str(img_local_paths / "metro_{}.svg")
(
GT(metro_mini4)
.fmt_image("case4", file_pattern=file_pattern)
.cols_align(align="right", columns="case4")
)
| name |
lines |
case4 |
| Argentine |
1 |
 |
| Bastille |
1, 5, 8 |
 |
| Bérault |
1 |
 |
| Champs-Élysées—Clemenceau |
1, 13 |
 |
| Charles de Gaulle—Étoile |
1, 2, 6 |
 |
Case 4 is undoubtedly one of the most powerful features of Great Tables. While mastering it may take some practice, we hope this example helps you render images effortlessly and effectively.
Rendering Images Anywhere
While GT.fmt_image() is primarily designed for rendering images in the table body, what if you need to display images in other locations, such as the header? In such cases, you can turn to the versatile vals.fmt_image().
vals.fmt_image() is a hidden gem in Great Tables. Its usage is similar to GT.fmt_image(), but instead of working directly with DataFrame columns, it lets you pass a string or a list of strings as the first argument, returning a list of strings, each representing an image. You can then wrap these strings with html(), allowing Great Tables to render the images anywhere in the table.
Preparations
We will create a Pandas DataFrame named metro_mini using the data dictionary. This will be used for demonstration in the following examples:
Show the Code
metro_mini = pd.DataFrame(data)
metro_mini
|
name |
lines |
| 0 |
Argentine |
1 |
| 1 |
Bastille |
1, 5, 8 |
| 2 |
Bérault |
1 |
| 3 |
Champs-Élysées—Clemenceau |
1, 13 |
| 4 |
Charles de Gaulle—Étoile |
1, 2, 6 |
Single Image
This example shows how to render a valid URL as an image in the title of the table header:
Multiple Images
This example demonstrates how to render two valid URLs as images in the title and subtitle of the table header:
metro_logo_url = "https://raw.githubusercontent.com/rstudio/gt/master/images/dataset_metro.svg"
logo_urls = [gt_logo_url, metro_logo_url]
1_gt_logo, _metro_logo = vals.fmt_image(logo_urls, height=100)
gt_logo, metro_logo = html(_gt_logo), html(_metro_logo)
(
GT(metro_mini)
.fmt_image("lines", path=img_url_paths, file_pattern="metro_{}.svg")
.tab_header(title=gt_logo, subtitle=metro_logo)
.cols_align(align="right", columns="lines")
.opt_stylize(style=4, color="gray")
)
- 1
-
Note that if you need to render images with different
height or width, you might need to make two separate calls to vals.fmt_image().
 |
 |
| name |
lines |
| Argentine |
 |
| Bastille |
 |
| Bérault |
 |
| Champs-Élysées—Clemenceau |
 |
| Charles de Gaulle—Étoile |
 |