Library / SDK
wilkox/gggenes avatar
wilkox/gggenes

gggenes turns a data frame of gene coordinates into a gene map in ggplot2

➡️️➡️️⬅️️➡️️ Draw gene arrow maps in ggplot2

604 stars51 forksRLicense varies

At a glance

What is it?
gggenes is a small R package that adds gene arrow geoms, subgene layers, labels and a theme to ggplot2, so a table of start and end coordinates becomes a readable gene map without hand drawing anything. It suits a genomicist who already lives in ggplot2, and it assumes a tidy data frame with one row per gene and numeric coordinates.
Who is it for?
gggenes fits an R user who already works in ggplot2 and has gene coordinates in a data frame, because the whole package is a set of geoms that read the standard xmin, xmax and y aesthetics and nothing else. It does not fit a workflow built on another plotting system, and it does not fit genomic data whose gene coordinates are on a scale that makes faceting impractical, since the alignment helpers work by adding dummy rows rather than by solving a layout.
Can I use it commercially?
Not without permission. GitHub finds no licence file in the repository, and without a licence all rights are reserved by default: you may read the code but not reuse it. Check the README, or ask the authors, before using it.
Is it still maintained?
Yes. The repository last received commits 110 days ago.
What is it written in?
Mainly R, according to GitHub's language statistics.

Answers come from the project's GitHub data, last synced on October 23, 2026, and from our analysis. They are not legal advice.

Editorial analysis

A geom that reads xmin, xmax and y and nothing else

gggenes is a ggplot2 extension for drawing gene arrow maps, and the whole idea fits in one paragraph.

`geom_gene_arrow()` is a ggplot2 geom that represents genes with arrows. The start and end locations of the genes within their molecules are mapped to the `xmin` and `xmax` aesthetics respectively, and those two values are what determine the direction in which the arrows point. The `y` aesthetic must be mapped to the molecules.

So the package is a coordinate convention, not a data model. You bring a table, you map four columns, and you get arrows.

The first example is as small as the idea.

``` r library(ggplot2) library(gggenes)

ggplot(example_genes, aes(xmin = start, xmax = end, y = molecule, fill = gene)) + geom_gene_arrow() + facet_wrap(~ molecule, scales = "free", ncol = 1) + scale_fill_brewer(palette = "Set3") ```

One dataset from the package, one aes block, one geom, a facet and a palette. There is no gene model object, no annotation object, no special class to construct.

For a genomicist this is the right shape, because the data usually already looks like that. A genome browser export, an annotation table from a pipeline, the result of a coordinate join: all of them are long tables with a molecule column and a start and end column, and this geom consumes them directly.

Free scales across facets, or the plot becomes unreadable

The documentation is unusually insistent about one point, and it is worth repeating because it is the thing that makes these plots work.

If you are drawing more than one molecule, and the numerical locations of the genes are not similar across molecules, you almost certainly want to facet the plot with `scales = "free"` to avoid drawing ridiculously large molecules with ridiculously tiny genes.

That is the failure mode. Genomic coordinates are absolute positions on a chromosome or plasmid, so a 5 kilobase plasmid and a 5 megabase chromosome produce axes that differ by three orders of magnitude. On a shared scale, the small molecule becomes a speck and its genes become invisible. With free scales each facet gets its own axis, and the plot is readable, at the cost that the panels are no longer directly comparable in length.

The examples use `facet_wrap(~ molecule, scales = "free", ncol = 1)`, which stacks the molecules vertically with one column. The ncol choice is a layout decision rather than a requirement, and the one column form is what makes the faceted plots read like a set of parallel tracks.

Everything else in the package is layered on top of that arrangement. The theme tidies it, the labels go inside the arrows, and the alignment dummies solve the problem of keeping one gene at the same vertical position across facets.

theme_genes exists because the default theme is too loud

The package includes a ggplot2 theme called `theme_genes`, and the reason it is there is stated plainly: because the resulting plot can look cluttered, a theme with some sensible defaults is provided.

``` r ggplot(example_genes, aes(xmin = start, xmax = end, y = molecule, fill = gene)) + geom_gene_arrow() + facet_wrap(~ molecule, scales = "free", ncol = 1) + scale_fill_brewer(palette = "Set3") + theme_genes() ```

The theme is a normal ggplot2 theme, so it composes with everything else, and it is a sensible default rather than a house style you have to fight. Gene maps drawn with dozens of narrow arrows on a small panel accumulate a lot of axis decoration, and the theme's job is to strip that back.

There is a broader point here about how the package is built. Every piece of it is a standard ggplot2 object: a geom, a theme, a helper function that returns a data frame. Nothing in gggenes is a plotting system of its own, and nothing in it is opaque. Once you have used it, you can adjust it with the same tools you would use on any other ggplot2 plot, because that is what it is.

Alignment dummies are a data frame, not a layout engine

The most interesting function in the package is `make_alignment_dummies()`, and its mechanism is worth understanding because it is unusual.

Often you will want a certain gene to be vertically aligned across the faceted molecules. `make_alignment_dummies()` generates a set of dummy genes that, if added to the plot with `geom_blank()`, will extend the range of each facet to visually align the selected gene across facets.

``` r dummies <- make_alignment_dummies( example_genes, aes(xmin = start, xmax = end, y = molecule, id = gene), on = "genE" )

ggplot(example_genes, aes(xmin = start, xmax = end, y = molecule, fill = gene)) + geom_gene_arrow() + geom_blank(data = dummies) + facet_wrap(~ molecule, scales = "free", ncol = 1) + scale_fill_brewer(palette = "Set3") + theme_genes() ```

The trick is that alignment is achieved by padding, not by layout. Since each facet has free scales, the axis range of a facet is determined by the data drawn in it. So to make the target gene appear at the same height in every facet, the package generates rows that extend each facet's range, and the target gene ends up in the same relative position in all of them.

The `id` aesthetic is how you tell it which column identifies a gene, and `on` names the gene to align on. The output is a plain data frame that you pass to `geom_blank()`, which draws nothing visible but still contributes to the panel's range.

It is a neat solution, and it is also a limited one. It aligns on a single named gene per call, and it works by widening the panel rather than by placing elements at computed coordinates.

Labels that shrink to fit inside the arrow

Labelling genes is where the package leans on a companion project.

To label individual genes you provide a `label` aesthetic and use `geom_gene_label()`. The label geom uses the ggfittext package to fit the label text inside the gene arrows, and the documentation points at the ggfittext documentation for details on how it resizes and reflows text to make it fit.

``` r ggplot( example_genes, aes(xmin = start, xmax = end, y = molecule, fill = gene, label = gene) ) + geom_gene_arrow(arrowhead_height = unit(3, "mm"), arrowhead_width = unit(1, "mm")) + geom_gene_label(align = "left") + geom_blank(data = dummies) + facet_wrap(~ molecule, scales = "free", ncol = 1) + scale_fill_brewer(palette = "Set3") + theme_genes() ```

The text fitting is the point. A gene name inside a 200 base pair arrow is unreadable at normal size, so the geom scales the label down, and if it has to, it reflows it across lines. The arrowhead size is set directly on the geom with the `arrowhead_height` and `arrowhead_width` arguments, in millimetre units, because a default arrowhead is much too large relative to a short gene.

The `align = "left"` argument positions the text inside the arrow body, and it is the kind of detail you end up adjusting per dataset. There is no automatic knowledge of which side of the arrow has room.

forward separates direction from coordinates

The `forward` aesthetic is a small addition with a real use case.

By default, gene arrows are drawn pointing in the direction implied by `xmin` and `xmax`. You can override that with the optional `forward` aesthetic, which the documentation says can be useful when the gene coordinates and orientation are encoded as separate variables.

If `forward` is TRUE, which is the default, or any value that coerces to TRUE such as 1, the gene is drawn pointing in the implied direction, from `xmin` to `xmax`. If it is FALSE, or any value that coerces to FALSE such as 0, the gene is drawn in the reverse of that implied direction.

``` r ggplot(example_genes, aes(xmin = start, xmax = end, y = molecule, fill = gene, forward = orientation)) + geom_gene_arrow() + facet_wrap(~ molecule, scales = "free", ncol = 1) + scale_fill_brewer(palette = "Set3") + theme_genes() ```

The situation it solves is a common one in annotation files. The coordinates are always written as start and end, so a gene on the reverse strand has start greater than end, and some pipelines normalise that before export. If your file keeps the original strand information in its own column, `forward` lets you say which way to draw the arrow without touching the coordinates.

The value is coerced to a logical, so the column can be 1 and 0 rather than TRUE and FALSE, which is convenient when the data comes straight out of a parser.

Subgenes and point features, and where the package stops

Beyond whole genes there are two more layers.

`geom_subgene_arrow()` highlights subgene segments such as protein domains or local alignments. It works similarly to `geom_gene_arrow()`, but in addition to `xmin` and `xmax` for the gene boundaries you need the aesthetics `xsubmin` and `xsubmax` for the subgene boundaries. The documentation is specific about the constraint: `geom_gene_arrow()` will produce pretty arrowheads as long as `xmin >= xsubmin` and `xmax >= xsubmax` for all subgenes, and subgenes that break the gene boundaries are skipped with a warning.

That is a real limitation with a stated behaviour, which is better than a silently wrong arrow. It also dictates the suggested usage, which is to draw the gene with no fill and then add a subgene layer over it.

``` r ggplot(example_genes, aes(xmin = start, xmax = end, y = molecule)) + facet_wrap(~ molecule, scales = "free", ncol = 1) + geom_gene_arrow(fill = "white") + geom_subgene_arrow(data = example_subgenes, aes(xmin = start, xmax = end, y = molecule, fill = gene, xsubmin = from, xsubmax = to), color="black", alpha=.7) + theme_genes() ```

Point features such as restriction sites or transcription start sites are drawn with `geom_feature()` and labelled with `geom_feature_label()`. Both take an optional `forward` aesthetic to indicate whether a feature is oriented and in what direction.

So the coverage is: gene arrows, subgene arrows, point features, labels for each, a theme, and an alignment helper. There is no support for drawing the molecule backbone itself as a distinct element, no strand tracks, no assembly summary, and no coordinate transformation. It draws the genes you give it, and the surrounding figure is your ggplot2 code.

Packaging, licence and who maintains it

gggenes is on CRAN, and the documentation distinguishes the two install paths clearly. The stable version comes from CRAN with `install.packages("gggenes")`, and the development version comes from GitHub with `devtools::install_github("wilkox/gggenes")`.

The badges show an R-CMD-check workflow, a CRAN status badge, and a lifecycle badge marked stable, which is the tidyverse lifecycle convention and means the package's interface is considered settled.

The homepage is wilkox.org/gggenes, and the repository is the standard R package tree: R/ for the source, man/ for the documentation, tests/, vignettes/, data/, inst/, a NAMESPACE and a DESCRIPTION, plus a _pkgdown.yml for the site and a NEWS.md for the changelog. There is an air.toml, the same formatter configuration used by several of the R projects in this ecosystem, a cran-comments.md file for the CRAN submission record, and a .prettierrc.

The package is published by the same author as ggfittext, the text fitting package that geom_gene_label() depends on, so the label geom and the fitting library are maintained together rather than being an unrelated third party dependency.

The version history is short. The listed release is 0.7.0 on 2026-07-05, and the last push was on the same day, so the CRAN version and the repository head coincide. The project metadata does not classify the licence, so the terms are not stated in the summary; the package's own DESCRIPTION file is where the licence for this project is recorded.

Editorial conclusion

gggenes fits an R user who already works in ggplot2 and has gene coordinates in a data frame, because the whole package is a set of geoms that read the standard xmin, xmax and y aesthetics and nothing else. It does not fit a workflow built on another plotting system, and it does not fit genomic data whose gene coordinates are on a scale that makes faceting impractical, since the alignment helpers work by adding dummy rows rather than by solving a layout. Before adopting it, install the stable CRAN version with install.packages("gggenes"), check that your data has one row per gene with numeric start and end columns, and decide early whether you need the faceting with free scales and the alignment dummies, because the package's own advice is that mismatched coordinate ranges across molecules are the first thing that goes wrong.

Frequently asked questions

How do I install gggenes?

Install the stable version from CRAN with install.packages("gggenes"), or the development version from GitHub with devtools::install_github("wilkox/gggenes").

Which aesthetics does geom_gene_arrow() need?

The start and end of each gene are mapped to xmin and xmax, and the y aesthetic must be mapped to the molecule. The direction the arrow points follows from the two x values.

How do I keep one gene aligned across faceted molecules?

Use make_alignment_dummies() with the id aesthetic naming the gene column and the on argument naming the gene to align on, then add the returned data to the plot with geom_blank(). The dummies extend each facet's range so the chosen gene appears at the same height everywhere.

How do I draw protein domains or local alignments?

Use geom_subgene_arrow(), which needs xsubmin and xsubmax for the subgene boundaries in addition to xmin and xmax. Subgenes that break the gene boundaries are skipped with a warning, and the suggested usage is a filled white gene arrow with the subgene layer on top.

Official sources

  1. Issues
  2. Project website
  3. README
  4. Releases
  5. wilkox/gggenes on GitHub
Add this badge to your README

If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.

Add this badge to your README

markdown
[![Hysen Labs](https://hysenlabs.com/badge/wilkox-gggenes.svg)](https://hysenlabs.com/projects/wilkox-gggenes)

Was this useful?