Usage

The simplest way to use react-msaview in a React app. Handles model creation, width measurement, and theming automatically.

npm install react-msaview @jbrowse/core@next mobx@7 mobx-react@10 \
  @jbrowse/mobx-state-tree@6 @mui/material@9 @mui/icons-material@9 \
  @emotion/react @emotion/styled react react-dom

The line pins the majors because the viewer shares mobx, mobx-state-tree and MUI with @jbrowse/core, and the app needs exactly one copy of each. With two copies of mobx-state-tree, the first render throws “Identifier types can only be instantiated as direct child of a model type”. With two copies of MUI, a theme built by one copy reaches components from the other, which also throws. The versions above are the ones @jbrowse/core 5 depends on, and react-msaview needs mobx 7. Core 5 is published under the next tag until its release, after which @jbrowse/core@5 replaces @next. Without the pins, npm resolves each peer to its own latest major and installs a second copy.

CI installs exactly this line outside the workspace with plain npm and renders the snippet below in a headless browser (scripts/npm-smoke.mjs), so a broken install line fails CI.

import { MSAViewer } from 'react-msaview'

export default function App() {
  // backticks (template literal) so the \n become real newlines; a plain
  // "..." JSX attribute would pass the literal characters \ and n instead
  return (
    <MSAViewer
      msa={`>human\nMKAANSE\n>mouse\nMKA-NSE`}
      tree="(human:0.1,mouse:0.2);"
      colorScheme="clustal"
    />
  )
}

Props:

PropTypeDescription
msastringAlignment text (FASTA, Stockholm, Clustal, A3M, EMF)
treestringNewick tree text
gffstringAnnotations to overlay (GFF3 text)
msaFilehandleFileLocationRemote file location for alignment
treeFilehandleFileLocationRemote file location for tree
gffFilehandleFileLocationRemote file location for domain GFF
colorSchemestringColor scheme name (see below)
heightnumberWidget height in pixels
colWidthnumberPer-column width in pixels (horizontal zoom)
rowHeightnumberPer-row height in pixels (vertical zoom)
allowedGappynessnumberHide columns at least this percent gaps (default 100, hide nothing)
relativeTostringRow name to diff every other row against; matches draw as .
drawTreebooleanDraw the phylogeny (default true); false leaves a label gutter
treeAreaWidthnumberFixed width of the tree/label gutter
autoTreeAreaWidthbooleanSize that gutter to the labels; pair with drawTree: false
columnTracksColumnTrackSpec[]Tracks supplied as data (see below)
highlightsHighlight[]Labeled highlights (see below)
highlightColumnsnumber[]Columns (0-based) under a persistent overlay
cladesClade[]Tree clades with a mark over them (see below)
residueMappingsResidueMapping[]Structure residue for each residue of a row
rowDataRecord<string, ...>Extra fields per row name, such as a lineage or a host
encodingsEncoding[]What the marks read: tipLabel, rowTint, branch, featureFill, featureLabel
rowPanelsRowPanelSpec[]Panels between the tree and the alignment, one cell per row (see below)
showBranchLenbooleanDraw branch lengths (default true); false draws a cladogram
residueEncoding'fill' | 'color'Which channel colorScheme paints: the cell (default) or the letter
regionRegionZoom to {row, start, end} residues, or {start, end} columns
hideHeaderbooleanLeave out the toolbar, for a page drawing its own controls
themestring | ThemeOptions'light' (default), 'dark', or MUI theme options merged over JBrowse’s
onCellHover(cell) => voidThe cell under the pointer (see below)
onCellClick(cell) => voidThe cell a click pinned, or undefined when a click clears it
onViewportChange(viewport) => voidThe alignment columns on screen
onModel(model) => voidThe model the viewer built, for the model API below

The viewer applies a changed prop to the mounted model, so a host can put a control on one without re-fetching the alignment. Each prop updates only its own setting, so the host’s next render keeps a change made inside the viewer, such as a scheme picked from the menu or a row dragged taller. The viewer compares the data layers (highlights, clades, columnTracks, residueMappings, rowData, encodings, rowPanels, highlightColumns) and the filehandles by content, so passing a freshly computed array or location object on every render costs nothing. A new msa, tree or gff string, or a filehandle pointing somewhere else, builds a new model and resets the view.

Events

onCellHover and onCellClick receive a Cell in the coordinates highlights use: column is the 1-based column of the file, counting hidden gappy columns, and residue is the 1-based position in that row’s own sequence, absent on a gap. A pointer over a track gives a column and no row. onViewportChange receives {startColumn, endColumn}, 1-based and inclusive, after each scroll, zoom and resize that changes them.

const [clicked, setClicked] = useState<Cell>()

return (
  <>
    <MSAViewer msa={msa} tree={tree} hideHeader onCellClick={setClicked} />
    {clicked?.residue ? (
      <p>
        {clicked.row} residue {clicked.residue} ({clicked.letter})
      </p>
    ) : null}
  </>
)

Data layers

highlights, columnTracks and residueMappings take data the host computed: a labeled band over a residue or column range, a bar, text or arc track above the alignment, and the residue-by-residue correspondence between a row and a structure. Each is also a model property, so it travels in a shared URL and the SVG export draws it.

<MSAViewer
  msa={msa}
  tree={tree}
  highlights={[
    { row: 'human', start: 248, end: 248, label: 'R248Q · 651/658 R' },
    { start: 40, end: 60, label: 'NES', color: 'rgba(0,120,255,0.25)' },
    { rows: ['beluga', 'dolphin'], label: 'frameshift carriers' },
  ]}
  columnTracks={[
    {
      id: 'dnds',
      name: 'dN/dS',
      kind: 'bar',
      values: dnds,
      max: 2,
      row: 'human',
    },
  ]}
/>

clades marks a clade of the tree: mrca names tips whose common ancestor is the clade, and tips is the leaf count the producer measured. A clade that resolves to a different leaf count is dropped, so a re-rooted tree loses the mark rather than putting it on the wrong clade. mark says what to draw: highlight fills the rows behind the clade across the tree and the alignment, bracket draws a bar and the record’s label in a gutter at the right of the tree, and collapse and focus open the viewer with the clade collapsed or with the rest of the tree hidden.

<MSAViewer
  msa={msa}
  tree={tree}
  clades={[
    {
      mrca: ['Gs/TW/TNC1/2015', 'Ck/TW/a174/2015'],
      tips: 47,
      mark: 'bracket',
      label: '2.3.4.4 H5Nx',
    },
  ]}
/>

rowData is a field table keyed by row name, and encodings says which mark reads which field: tipLabel colors the tip labels in the tree, rowTint washes the row across the tree gutter and the alignment, and branch colors a tree edge whose tips all share one value. The scale is a named palette or a color per value, and a value the table gives no color keeps the plain mark. featureFill and featureLabel read a field of the annotations, coloring and naming each span of the overlay.

<MSAViewer
  msa={msa}
  tree={tree}
  rowData={{
    'A/duck/Anhui/1/2013': { clade: '2.3.4.4b' },
    'A/chicken/Taiwan/a174/2015': { clade: '2.3.2.1c' },
  }}
  encodings={[
    { channel: 'tipLabel', field: 'clade', scale: { palette: 'set1' } },
    { channel: 'rowTint', field: 'clade' },
  ]}
/>

rowPanels draws the same table as columns of colored cells between the tree and the alignment, which is ggtree’s gheatmap. Each strip record reads one field, takes its own scale, and carries a rotated header over its column. Strips over one field share that field’s legend.

<MSAViewer
  msa={msa}
  tree={tree}
  rowData={rowData}
  rowPanels={[
    { kind: 'strip', field: 'HA', scale: { palette: 'set1' }, width: 12 },
    { kind: 'strip', field: 'NA', header: 'NA segment' },
  ]}
/>

A features record draws the spans the GFF carries in a panel of its own, as arrows where a gene has a strand. x: 'position' draws each row in its own residue positions on one linear scale, so the figure below needs no msa: the tree and the gene arrows fill the view. The align transform shifts each row so that genE starts at one x, and a genome lacking it keeps its own origin, which is gggenes’ make_alignment_dummies.

<MSAViewer
  tree={tree}
  gff={gff}
  rowPanels={[
    {
      kind: 'features',
      x: 'position',
      width: 320,
      header: 'neighborhood',
      encoding: {
        color: { field: 'Name', scale: { palette: 'set1' } },
        label: 'Name',
      },
      transform: [{ type: 'align', on: 'genE' }],
    },
  ]}
/>

The layers reference lists every field of every layer and the coordinate rules they share. At runtime model.setHighlights(list), model.setClades(list), model.setColumnTracks(tracks), model.setRowData(table) and model.setRowPanels(panels) replace what the props set.

One panel in your own page

A purpose-built page usually shows less than the standalone app. With the phylogeny off, the gutter holds only the row labels and sizes itself to them:

const [expanded, setExpanded] = useState(false)
const [diff, setDiff] = useState(false)

return (
  <>
    <button onClick={() => setDiff(d => !d)}>Diff vs reference</button>
    <button onClick={() => setExpanded(e => !e)}>
      {expanded ? 'Collapse' : 'Expand'}
    </button>
    <MSAViewer
      msaFilehandle={{ uri: msaUrl, locationType: 'UriLocation' }}
      gffFilehandle={{ uri: gffUrl, locationType: 'UriLocation' }}
      colorScheme="clustalx_dna"
      drawTree={false}
      autoTreeAreaWidth
      relativeTo={diff ? referenceRow : undefined}
      height={expanded ? 760 : 420}
    />
  </>
)

Both toggles update the mounted viewer, so flipping one does not re-fetch the alignment. Use drawTree={false} with autoTreeAreaWidth whenever there is no tree to draw, such as for a reference-projected reconstruction with no meaningful guide tree. Without autoTreeAreaWidth the gutter keeps its full default width.

The component pulls in @jbrowse/core, MUI and mobx and renders to canvas, so in a server-rendered app (Next.js, Astro, Remix) load it on the client only, with React.lazy inside a Suspense or the framework’s equivalent.

Advanced: model-based API

For state the props do not cover, such as hiding gappy columns, collapsing clades or reading what the user selected, use the model. onModel hands over the model MSAViewer built, which keeps the width measurement and theme the component provides. To build the model yourself, use MSAModelF and render it with MSAView, which leaves setting the width to you:

import { MSAView, MSAModelF } from 'react-msaview'

export default function App() {
  const model = MSAModelF().create({
    type: 'MsaView',
    data: {
      msa: 'string containing stockholm, clustalw, or multi-fasta msa here',
      tree: 'string containing newick formatted tree here',
    },
  })

  model.setWidth(1800)

  return (
    <div style={{ border: '1px solid black', margin: 20 }}>
      <MSAView model={model} />
    </div>
  )
}

The model exposes actions for programmatic control:

model.setColorSchemeName('clustal')
model.setRowHeight(20)
model.setColWidth(16)
model.toggleCollapsed('node-id')
model.fit() // fit both axes

// transient highlights, keyed by owner, so a structure viewer's hover and a
// genome view's each clear only their own. Not persisted, unlike the
// `highlights` prop.
model.applyHighlight('protein3d', [{ row: 'human', start: 58, end: 58 }])
model.clearHighlight('protein3d')

Using react-msaview in a plain HTML file with UMD bundle

mount renders MSAViewer into an element and takes the same props. update merges new props over the current ones, and destroy unmounts the viewer.

<html>
  <head>
    <script
      crossorigin
      src="https://unpkg.com/react-msaview/bundle/index.js"
    ></script>
  </head>
  <body>
    <div id="viewer"></div>
    <p id="clicked"></p>
    <script>
      const { mount } = window.ReactMSAView
      const viewer = mount(document.getElementById('viewer'), {
        msaFilehandle: { uri: 'http://path/to/msa.stock' },
        treeFilehandle: { uri: 'http://path/to/tree.nh' },
        height: 400,
        onCellClick: cell => {
          document.getElementById('clicked').textContent = cell
            ? `${cell.row} residue ${cell.residue}`
            : ''
        },
      })
      viewer.update({ colorScheme: 'clustal' })
    </script>
  </body>
</html>

The bundle also exports MSAModelF, MSAView, React and createRoot for a page that builds the model itself, as in the model-based API above.

As a custom element

defineMsaElement() registers <jbrowse-msa>, which renders MSAViewer from attributes and properties, for a page that writes HTML instead of JavaScript components.

<script src="https://unpkg.com/react-msaview/bundle/index.js"></script>
<script>
  window.ReactMSAView.defineMsaElement()
</script>

<jbrowse-msa
  msa-url="https://example.org/family.stock"
  tree-url="https://example.org/family.nh"
  color-scheme="clustalx_protein_dynamic"
  height="400"
  hide-header
></jbrowse-msa>

Attributes: msa-url, tree-url, gff-url, color-scheme, height, hide-header, theme, tree-area-width and reference-row. Properties: msa, tree and gff text, highlights, columnTracks and residueMappings. The element dispatches cell-hover, cell-click and viewport-change events whose detail is the value the matching MSAViewer callback receives.

Inside a Nightingale <nightingale-manager>, the element registers with the manager like a Nightingale component. It follows the display-start, display-end and highlight attributes the manager writes, and reports its own range and hovered residue back as change events. Nightingale positions count the residues of one protein, so reference-row names the alignment row that protein is, and the element converts between its residues and the alignment’s columns. Give the manager reflected-attributes="display-start,display-end,highlight". The examples page has a live one beside Nightingale’s navigation and sequence tracks.

R package (msaviewr)

An R htmlwidget that takes ape, Biostrings, ggtree, and treeio objects directly. See the R package guide for installation and examples.

Color schemes

Protein: clustal, maeditor, lesk, cinema, flower, clustalx_protein, jalview_taylor, jalview_zappo, jalview_hydrophobicity, jalview_buried, jalview_prophelix, jalview_propstrand, jalview_propturn

Nucleotide: nucleotide, jbrowse_dna, rainbow_dna, clustalx_dna

Dynamic (per-column): clustalx_protein_dynamic, percent_identity_dynamic

API

See the auto-generated API docs: packages/lib/apidocs/MsaView.md

The model source is in packages/lib/src/model.ts. react-msaview uses MobX-state-tree models; components wrapped with observer from mobx-react automatically re-render when observed model properties change.

See also