cnsplots: journal figure styling without hand-tuning matplotlib
🎨 Toolkit for generating publication-quality plots for Cell, Nature and Science journals
At a glance
- What is it?
- cnsplots is a Python plotting library that presets the figure styling that Cell, Nature and Science submissions expect, wraps more than 25 plot types on top of matplotlib, and exports SVG with editable fonts. It is a Beta library with a large dependency surface.
- Who is it for?
- cnsplots fits a bioinformatics or general scientific team that has to produce many journal figures and does not want to hand-tune rcParams, panel sizes, and label placement for every submission.
- Can I use it commercially?
- Yes. BSD-3-Clause is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
- Is it still maintained?
- Yes. The repository last received commits 25 days ago.
- What is it written in?
- Mainly Python, 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
Why figure styling deserves its own package
Getting a figure accepted by a journal is not only about the data. Sizes have to be expressed in the units the production team uses, panels have to line up, text has to stay editable, and the palette has to survive a print check. Doing that by hand means a set of rcParams tweaks that live in one project and nowhere else, and every new figure inherits them by accident rather than by design. cnsplots takes that work out of the plotting call and puts it in a library. The README frames it as a tool for creating publication ready scientific figures with minimal code, so the intent is that the styling decisions are made once and then reused. The audience is specific: people preparing figures for Cell, Nature and Science, mostly in bioinformatics, where the data side of a figure is already complicated and the presentation side should not add work. The package sits on top of matplotlib and the README states it is fully compatible with seaborn, so it is an addition to a matplotlib workflow rather than a replacement of it.
Installing from PyPI and the bundled agent skill
The normal install is a single command from PyPI, and the README states that it brings in every supported plotting and scientific integration. Imports are described as lazy, so the heavier backends are only loaded when their APIs are first called, which keeps the import cost of a simple figure down even though the dependency list is long.
pip install cnsplotsThe package also ships an agent skill so that Codex and Claude Code build plots with the package's current workflow and API. By default the skill is installed for both agents at user scope, and the agent and scope options narrow that down, for example targeting a single agent or the current project. The same command family also reports status, which gives the destination, the installed version, and whether the installed content matches the bundled skill, and it can overwrite an installation or remove one.
cnsplots skill install
cnsplots skill install --agent codex
cnsplots skill install --agent claude --scope project
cnsplots skill status
cnsplots skill uninstall --agent claude --scope projectThe README is specific about the update behaviour: updates overwrite current packaged files and remove unchanged obsolete files, while unrelated files and modified obsolete files are preserved, and uninstall removes only unchanged managed files, so local edits survive. The install guide at docs/installation.md covers ownership tracking, legacy installations, and exit codes. For a development checkout the README tells you to install uv first, then clone the repository and run the make install target, which syncs the package with its development and documentation dependencies:
git clone https://github.com/faridrashidi/cnsplots
cd cnsplots
make installA first figure from a built-in dataset to an SVG file
The quick start in the README is short enough to read in one pass. A dataset is loaded through the package's own dataset helper, a figure is created with an explicit width and height in points, a plot is drawn, and the result is written out as a vector graphic. Nothing here returns a matplotlib axes object for you to configure by hand, and that is the point of the design: the call sequence is figure, plot, save, and each step has defaults chosen for a journal.
import cnsplots as cns
# Load example data
df = cns.datasets.load_dataset("tips")
# Create a figure (width, height in points)
fig = cns.figure(width=100, height=150)
# Create a publication-ready boxplot
cns.boxplot(data=df, x="day", y="total_bill")
# Save as vector graphic
cns.savefig("figure.svg", fig=fig)The dimensions are points, which matters because journals specify figure size in physical units, so a figure defined at 100 by 150 points has a known physical size on the page. The output format is SVG, and the README highlights the reason to prefer it for a submission workflow: the fonts stay editable rather than being converted to paths, so a designer can reopen the file in Adobe Illustrator and adjust it. The same file also offers statistical comparison annotations, where pairs of groups are passed to the boxplot call and the significance marks are added to the figure rather than computed and drawn by hand.
Multipanel layouts that label their own panels
Most journal figures are grids, and the usual pain is that the letter labels drift away from the panels when the layout changes. The multipanel helper handles that part. You create a multipanel object with a maximum width, then declare each panel in turn with a letter and a width and height, and draw into the active panel.
import cnsplots as cns
mp = cns.multipanel(max_width=540)
# Panel A
mp.panel("A", width=150, height=150)
cns.boxplot(data=df1, x="group", y="value")The letters A, B, C and so on are assigned by the helper rather than typed into each axes, which is what keeps a multi-panel figure consistent when a panel is resized or a new one is added. Widths and heights are again in points, and the maximum width constrains the total figure so panels cannot silently overflow the printable area. The repository ships a matching example file, examples/multipanel.py, alongside one example per plot family such as examples/heatmapplot.py, examples/forestplot.py, and examples/gseaplot.py, which is a faster way to see what a given plot type expects than the prose documentation does.
A dependency list that reaches far past plotting
Look at what gets installed with the package and the real scope becomes clear. Beyond matplotlib, numpy, pandas, scipy, seaborn, patsy, and natsort, the dependency list includes anndata and scanpy for single cell data, biopython, lifelines for survival analysis, gseapy for enrichment, scikit-learn and statsmodels for statistics, pycomplexheatmap for clustered heatmaps, upsetplot for intersection plots, matplotlib-venn for Venn diagrams, statannotations for the significance brackets, comprisk for cumulative incidence, and palettable for the color maps. That is the price of the 25 plus plot types the README advertises, which span survival and cumulative incidence curves, ROC curves, forest plots, volcano and GSEA plots, confusion matrices, hierarchical heatmaps, dot plots for enrichment, Sankey diagrams, and QQ and slope plots. The lazy import behaviour described in the install section is the mitigation, but the install itself is heavy, and a clean environment will pull a substantial scientific stack even for a figure that uses none of it. The listed dependencies also pin a range for a few packages, pycomplexheatmap above 1.8.4 and below 1.9, seaborn at 0.13.2 or newer, and matplotlib at 3.10 or newer, so a host environment with older pins will conflict.
Palettes, point based sizing, and editable SVG output
Two things separate a figure that looks publication ready from one that looks like a default matplotlib plot, and the library addresses both. The first is colour. The palettes are grouped the way the plotting literature groups them: qualitative sets including Cell, Nature, Science, Ecotyper1 to 6, Set1 to 3, Tableau and Bold, sequential options such as Parula, gnuplot and custom gradients, and diverging options such as BlueRed, BuRd_custom and OrBu_custom. The second is physical sizing, where dimensions can be given in points, inches, or millimeters, so a figure can be built to the width a journal specifies. The export format follows from the same goal. SVG output keeps fonts editable, which the README calls out as Adobe Illustrator compatibility, and the alternative of text converted to paths is exactly what a production team does not want to receive. The example gallery under the documentation site, at cnsplots.farid.one, collects rendered examples per plot type, and the repository also carries a dedicated examples/palettes.py file if you want the list of swatches in code.
Beta status, release cadence, and what upgrading costs
The project states its maturity plainly. The packaging metadata classifies cnsplots as a Beta, with the audience listed as Science and Research, and the operating system listed as OS Independent. The version history is fast rather than slow. Three minor releases landed in under three months: v0.6.0 on 2026-07-29, v0.7.0 on 2026-08-28, and v0.8.0 on 2026-09-28, with the last push on 2026-09-28 alongside the newest tag. Rapid minor version movement is what you expect in a Beta library, and it means a project that pins cnsplots will need to read the changelog before taking a minor bump, because a figure library can change default rendering without changing any API a caller touches. The repository is not archived. The licence is BSD-3-Clause, a permissive licence that lets you use the library in commercial and closed source work as long as the copyright and licence notice are retained, and it adds no requirement to release anything built on top of it. Development work is driven by a Makefile with targets for lint, test, test-visual, doc, doc-linkcheck, install, and release, so the visual regression suite is part of the normal workflow rather than an afterthought.
Hand-tuned matplotlib styles versus preset journal styles
The alternative to this package is the one most teams already have: a matplotlib style sheet in the repository, a few seaborn calls, and a checklist of figure sizes checked by hand before submission. The difference in approach is where the knowledge lives. A home grown style sheet is a set of overrides that grows one rule at a time, tuned by whoever made the figure that revealed the gap, and it has to be carried forward by every person who touches the code. cnsplots moves those rules into a versioned library with a published API, a point based sizing convention, palette names, and a maintained example gallery, so a new member of a team writes the same figure call rather than rediscovering the settings. The cost of that choice is the dependency stack described earlier, and the loss of the final say on rendering, since a journal that rejects a figure can force you to override the library's defaults and land back where you started. Because the package is built on matplotlib and is fully compatible with seaborn, that escape route stays open: every function here is a convenience layer over an API you can drop down to at any point.
Editorial conclusion
cnsplots fits a bioinformatics or general scientific team that has to produce many journal figures and does not want to hand-tune rcParams, panel sizes, and label placement for every submission. It does not fit a project that must keep a slim dependency footprint, since the install pulls in scanpy, anndata, lifelines, gseapy, scikit-learn and statsmodels even when you only draw a boxplot, nor does it fit a team that needs a stable API, because the project describes itself as Beta and its version has moved through three minor releases in under three months. Before adopting it, run the visual regression target, make test-visual, against your own figures, because pytest-mpl compares rendered output, and check that your Python version falls inside the >=3.10,<4.0 range declared in pyproject.toml.
Frequently asked questions
How do I install cnsplots?
The README documents a single install from PyPI, pip install cnsplots, which brings in every supported plotting and scientific integration with lazy imports so heavy backends load only when used. The package requires Python 3.10 or newer and below 4.0 according to pyproject.toml.
How do I install the cnsplots skill for Codex or Claude Code?
Run cnsplots skill install, which by default targets both agents at user scope. You can narrow it with cnsplots skill install --agent codex or cnsplots skill install --agent claude --scope project, and use cnsplots skill status, cnsplots skill install --force, or cnsplots skill uninstall with the same agent and scope options to inspect, update, or remove an installation.
Does cnsplots work with seaborn and matplotlib?
cnsplots is built on matplotlib and the README states it is fully compatible with seaborn. Figures are created through the package's own figure and plotting calls, and you can still drop down to raw matplotlib for anything the library does not cover.
How do I save a cnsplots figure for Adobe Illustrator?
Save the figure as SVG with cns.savefig, for example cns.savefig("figure.svg", fig=fig). The README highlights that SVG exports keep fonts editable instead of converting text to paths, which is what makes the file workable in Illustrator.
What are the development commands for cnsplots?
The Makefile provides make install to sync the development environment and install hooks, make test to run the unit tests, make test-visual to run the visual regression tests, and make doc to build the documentation. Each of them runs through uv, and the README tells contributors to install uv first and clone the repository before running make install.
Official sources
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.
[](https://hysenlabs.com/projects/faridrashidi-cnsplots)Was this useful?