Skip to main content
Each flextable cell contains a single paragraph built from one or more chunks. A chunk is the smallest content unit — a piece of text, an image, an equation, or a special field. as_paragraph() assembles chunks into a paragraph; compose() (or its alias mk_par()) writes that paragraph into the table.

The composition model

1

Create chunks

Use chunk functions such as as_chunk(), as_b(), minibar(), or as_equation() to produce chunk objects.
2

Assemble a paragraph

Pass the chunks to as_paragraph(). Plain strings are automatically converted to chunks.
3

Write to the table

Pass the paragraph to compose() / mk_par() as the value argument, selecting which cells to update with i, j, and part.
library(flextable)
library(officer)

ft <- flextable(head(iris))

ft <- compose(
  ft,
  j = "Sepal.Length",
  value = as_paragraph(
    "Sepal.Length value is ",
    as_chunk(Sepal.Length, props = fp_text(color = "red"))
  ),
  part = "body"
)
ft <- autofit(ft)
ft
mk_par is a direct alias for compose provided to avoid a name conflict with the purrr package:
ft <- flextable(head(cars, n = 5), col_keys = c("speed", "dist", "comment"))
ft <- mk_par(
  x = ft, j = "comment",
  i = ~ dist > 9,
  value = as_paragraph(
    colorize(as_i("speed: "), color = "gray"),
    as_sup(sprintf("%.0f", speed))
  )
)
ft <- set_table_properties(ft, layout = "autofit")
ft

The use_dot mode

When use_dot = TRUE, each column is processed individually. The column value is available as . inside as_paragraph(), which lets you apply the same expression to several columns at once:
set.seed(8)
dat <- iris[sample.int(n = 150, size = 10), ]
dat <- dat[order(dat$Species), ]

ft <- flextable(dat)
ft <- mk_par(ft,
  j = ~ . - Species,
  value = as_paragraph(
    minibar(., barcol = "white", height = .1)
  ),
  use_dot = TRUE
)
ft <- theme_vader(ft)
ft <- autofit(ft)
ft

Text chunk functions

as_chunk(x, props, formatter)

The general-purpose text chunk. Pass an fp_text_default() or officer::fp_text() object to props to set font properties:
ft <- flextable(head(iris))
ft <- compose(ft,
  j = "Sepal.Length",
  value = as_paragraph(
    as_chunk(Sepal.Length, props = fp_text_default(color = "red"))
  ),
  part = "body"
)
When props is NULL, the cell’s default formatting applies.

Convenience chunk shortcuts

These functions wrap as_chunk() and set a single property:
FunctionEffect
as_b(x)Bold
as_i(x)Italic
as_strike(x)Strikethrough
as_sub(x)Subscript vertical alignment
as_sup(x)Superscript vertical alignment
colorize(x, color)Font color
as_highlight(x, color)Background highlight (shading)
Each accepts either a raw value or an existing chunk object:
ft <- flextable(head(iris), col_keys = c("dummy"))
ft <- compose(ft,
  i = 1, j = "dummy", part = "header",
  value = as_paragraph(
    as_sub("Sepal.Length"),
    " / ",
    as_sup("Sepal.Width")
  )
)
ft <- autofit(ft)
ft

as_bracket(..., sep, p, s)

Pastes values together and wraps the result in brackets. Useful for confidence intervals:
ft <- flextable(
  head(iris),
  col_keys = c("Species", "Sepal", "Petal")
)
ft <- compose(ft,
  j = "Sepal",
  value = as_paragraph(as_bracket(Sepal.Length, Sepal.Width))
)
ft <- compose(ft,
  j = "Petal",
  value = as_paragraph(as_bracket(Petal.Length, Petal.Width))
)
ft
Default separators: sep = ", ", p = "(", s = ")". Override any of them:
as_bracket(lo, hi, sep = "–", p = "[", s = "]")
hyperlink_text(x, props, formatter, url) creates a clickable text chunk:
dat <- data.frame(
  col = "Google it",
  href = "https://www.google.fr/search?source=hp&q=flextable+R+package",
  stringsAsFactors = FALSE
)

ftab <- flextable(dat)
ftab <- compose(
  x = ftab, j = "col",
  value = as_paragraph(
    "This is a link: ",
    hyperlink_text(x = col, url = href)
  )
)
ftab
Hyperlinks in Word output require the officedown package in an R Markdown context.

Equations

as_equation(x, width, height) embeds a MathJax / LaTeX equation. The equatags package is required:
if (require("equatags")) {
  eqs <- c(
    "(ax^2 + bx + c = 0)",
    "a \\ne 0",
    "x = {-b \\pm \\sqrt{b^2-4ac} \\over 2a}"
  )
  df <- data.frame(formula = eqs)

  ft <- flextable(df)
  ft <- compose(
    x = ft, j = "formula",
    value = as_paragraph(as_equation(formula, width = 2, height = .5))
  )
  ft <- align(ft, align = "center", part = "all")
  ft <- width(ft, width = 2)
  ft
}
Equations render to MathML in Word and HTML, and to LaTeX math mode in PDF.

Word field codes

as_word_field(x, props, width, height) inserts a Word field code such as page numbers or cross-references. Only affects Word (docx) output; other formats ignore it:
# Add a running page number to a header row
pp_docx <- function(x) {
  x <- add_header_lines(x, "Page ")
  x <- append_chunks(
    x = x, i = 1, part = "header", j = 1,
    as_word_field(x = "Page")
  )
  align(x, part = "header", align = "left")
}

ft <- flextable(cars)
ft <- autofit(ft)
ft <- pp_docx(ft)
Field values are inserted but not computed until you open the document in Word and press F9 (macOS: Fn + F9) to refresh them.
Apply Word-only modifications through post_process_docx to keep your code clean when targeting multiple output formats:
set_flextable_defaults(post_process_docx = pp_docx)

Quarto inline markdown

as_qmd(x, display) embeds Quarto markdown content — cross-references, bold/italic, inline code, links, and math — inside a cell:
1

Install the extension

Run once per Quarto project:
flextable::use_flextable_qmd()
2

Add the filter to your YAML

filters:
  - flextable-qmd
For Word output, also add the post-render filter:
filters:
  - flextable-qmd
  - at: post-render
    path: _extensions/flextable-qmd/unwrap-float.lua
3

Use as_qmd() in cells

dat <- data.frame(
  label = c("Bold", "Link", "Code"),
  content = c(
    "This is **bold** text",
    "Visit [Quarto](https://quarto.org)",
    "Use `print()` here"
  )
)
ft <- flextable(dat)
ft <- mk_par(ft, j = "content",
  value = as_paragraph(as_qmd(content)))
ft
as_qmd() is for inline markdown only. Block-level elements (headings, lists, fenced code blocks) are not supported inside a cell.

Appending and prepending chunks

When you only need to add content at the start or end of existing cell content — without replacing it — use append_chunks() or prepend_chunks():

append_chunks(x, i, j, ..., part)

Adds chunks after the existing content:
img.file <- file.path(R.home("doc"), "html", "logo.jpg")

ft <- flextable(head(cars))
ft <- append_chunks(ft,
  i = c(1, 3, 5),
  j = 1,
  as_chunk(" "),
  as_image(src = img.file, width = .20, height = .15)
)
ft <- set_table_properties(ft, layout = "autofit")
ft

prepend_chunks(x, i, j, ..., part)

Adds chunks before the existing content:
ft <- flextable(head(iris))
ft <- prepend_chunks(
  ft,
  i = 1, j = 1,
  colorize(as_b("Hello "), color = "red"),
  colorize(as_i("World"), color = "magenta")
)
ft
Both functions accept the same i, j, and part selectors as compose().

Building chunk data frames programmatically

chunk_dataframe(...) is the low-level constructor that all chunk functions call internally. Use it when building chunk objects from an external package or when you need full control over every column:
# Plain text with explicit properties
chunk_dataframe(
  txt = c("any text", "other text"),
  bold = c(TRUE, TRUE)
)

# Full control over all text properties
chunk_dataframe(
  txt = c("any text", "other text"),
  font.size = c(12, 10),
  italic = c(FALSE, TRUE),
  bold = c(FALSE, TRUE),
  color = c("black", "red"),
  shading.color = c("transparent", "yellow"),
  font.family = c("Arial", "Arial")
)

# Image pattern
chunk_dataframe(width = width, height = height, img_data = files)
The return value has class "chunk" and can be passed directly to as_paragraph().

Inspecting table content

Three functions expose the internal data structures of a flextable for debugging or programmatic manipulation:
FunctionReturns
information_data_chunk(x)One row per chunk per cell, with all text and image properties plus .part, .row_id, .col_id, .chunk_index.
information_data_paragraph(x)One row per cell, with paragraph-level formatting (alignment, padding, spacing) and position columns.
information_data_cell(x)One row per cell, with cell-level formatting (background, borders, vertical alignment) and position columns.
ft <- as_flextable(iris)
head(information_data_chunk(ft))
head(information_data_paragraph(ft))
head(information_data_cell(ft))
These functions expose internal data structures that may change between versions. Use them for debugging and inspection, not as a stable API.