JBrowse 2 · React App examples

Comparative views

Synteny and dotplot views, declaratively and through the imperative mount.

On this page: Linear synteny view · Dotplot view · Synteny via the imperative mount · Multi-way linear synteny view

Linear synteny view

Compare two assemblies with a PAF synteny track.

A react-app2 session holds any number of views of any type. Each launches the same declarative way — a views entry with a type and an init. init is the same shape JBrowse Web serializes into its ?session=spec-… URL parameter, so these examples are the programmatic equivalent of those URLs.

LinearSyntenyView puts two linear genome views one above the other with a ribbon for the synteny features between them (PAF, MUMmer, …). init names the two member assemblies and the track that ties them together.

Fields are per view type under docs/models — here LinearSyntenyView. To prepare your own alignment, see the synteny visualization tutorial.

View source — 43 lines
import { JBrowse } from '@jbrowse/react-app2'

const base = 'https://jbrowse.org/code/jb2/main/test_data/volvox'

// two volvox variants (the second has a deletion) and a PAF aligning them
const assemblies = [
  { name: 'volvox', uri: `${base}/volvox.2bit` },
  { name: 'volvox_del', uri: `${base}/volvox_del.fa` },
]

const tracks = [
  {
    type: 'SyntenyTrack',
    trackId: 'volvox_del.paf',
    name: 'volvox_del.paf',
    assemblyNames: ['volvox', 'volvox_del'],
    category: ['Synteny'],
    adapter: {
      type: 'PAFAdapter',
      uri: `${base}/volvox_del.paf`,
      targetAssembly: 'volvox',
      queryAssembly: 'volvox_del',
    },
  },
]

export default function SyntenyExample() {
  return (
    <JBrowse
      assemblies={assemblies}
      tracks={tracks}
      views={[
        {
          type: 'LinearSyntenyView',
          init: {
            views: [{ assembly: 'volvox' }, { assembly: 'volvox_del' }],
            tracks: ['volvox_del.paf'],
          },
        },
      ]}
    />
  )
}

Dotplot view

A self-vs-self volvox dotplot.

DotplotView compares two assemblies as a 2D dotplot. init.views lists the two assemblies and tracks the synteny tracks; self-vs-self is allowed, which is what this demo does.

Fields: DotplotView. The general { type, init } pattern is on Linear synteny view.

View source — 39 lines
import { JBrowse } from '@jbrowse/react-app2'

const base = 'https://jbrowse.org/code/jb2/main/test_data/volvox'

const assemblies = [{ name: 'volvox', uri: `${base}/volvox.2bit` }]

// a fake self-vs-self PAF, so both dotplot axes are the same assembly
const tracks = [
  {
    type: 'SyntenyTrack',
    trackId: 'volvox_fake_synteny',
    name: 'volvox_fake_synteny',
    assemblyNames: ['volvox', 'volvox'],
    category: ['Synteny'],
    adapter: {
      type: 'PAFAdapter',
      uri: `${base}/volvox_fake_synteny.paf`,
      assemblyNames: ['volvox', 'volvox'],
    },
  },
]

export default function DotplotExample() {
  return (
    <JBrowse
      assemblies={assemblies}
      tracks={tracks}
      views={[
        {
          type: 'DotplotView',
          init: {
            views: [{ assembly: 'volvox' }, { assembly: 'volvox' }],
            tracks: ['volvox_fake_synteny'],
          },
        },
      ]}
    />
  )
}

Synteny via the imperative mount

Mount the app imperatively with createApp(), the primitive non-React hosts wrap.

createApp(element, options) mounts the same engine as <JBrowse> with no React in its signature — the multi-view counterpart to createLinearGenomeView, and the primitive non-React hosts (Jupyter anywidgets, R htmlwidgets, plain <script> pages) wrap. react and react-dom are still peers: it saves you writing JSX and managing a React root, not React itself.

One declarative views list reaches every view type, in the same { type, init } shape the JSX synteny example uses. The returned controller adds views (controller.addView({ type, init })) and tears down (controller.destroy()).

The stylesheet import is required — without it the view manager’s tabs render unstyled. A host with no CSS loader can link node_modules/@jbrowse/react-app2/dist/styles.css instead.

View source — 57 lines
import { createApp } from '@jbrowse/react-app2'

const base = 'https://jbrowse.org/code/jb2/main/test_data/volvox'

const assemblies = [
  { name: 'volvox', uri: `${base}/volvox.2bit` },
  { name: 'volvox_del', uri: `${base}/volvox_del.fa` },
]

const tracks = [
  {
    type: 'SyntenyTrack',
    trackId: 'volvox_del.paf',
    name: 'volvox_del.paf',
    assemblyNames: ['volvox', 'volvox_del'],
    category: ['Synteny'],
    adapter: {
      type: 'PAFAdapter',
      uri: `${base}/volvox_del.paf`,
      targetAssembly: 'volvox',
      queryAssembly: 'volvox_del',
    },
  },
]

// Every other example on this site uses the <JBrowse> React component. This one
// uses `createApp` instead — the framework-agnostic imperative mount that
// non-React hosts (anywidget, R htmlwidgets, vanilla JS) wrap. It takes the same
// declarative `views` list, so a synteny view is one `{ type, init }` entry. A
// cleanup-returning ref bridges the imperative mount into React: it builds the
// app when the div attaches and disposes it when the div unmounts.
export default function CreateAppSynteny() {
  return (
    <div
      ref={el => {
        if (el) {
          const controller = createApp(el, {
            assemblies,
            tracks,
            views: [
              {
                type: 'LinearSyntenyView',
                init: {
                  views: [{ assembly: 'volvox' }, { assembly: 'volvox_del' }],
                  tracks: ['volvox_del.paf'],
                },
              },
            ],
          })
          return () => {
            controller.destroy()
          }
        }
      }}
    />
  )
}

Multi-way linear synteny view

Stack four E. coli strains in one synteny view, all backed by a single all-vs-all PAF.

A LinearSyntenyView is not limited to two genomes: give init.views three or more assemblies and it stacks them as rows, each adjacent pair joined by a ribbon. Here four E. coli strains (K-12, Sakai, CFT073, NCTC86), the pangenome demo from the all-vs-all synteny tutorial.

With N rows there are N−1 bands, so tracks is an array per bandtracks[i] connects views[i] and views[i+1]. Because a single all-vs-all PAF aligns every strain to every other, one SyntenyTrack over an AllVsAllPAFAdapter backs all three bands:

tracks: [['ecoli_ava'], ['ecoli_ava'], ['ecoli_ava']]

minAlignmentLength hides the short minimap2 alignments so the shared backbone reads as clean ribbons. For separate pairwise files, give each band its own PAF. Fields: LinearSyntenyView.

View source — 63 lines
import { JBrowse } from '@jbrowse/react-app2'

// Four E. coli strains aligned all-vs-all (the E. coli pangenome demo from the
// all-vs-all synteny tutorial). Data hosted at jbrowse.org/demos/ecoli_pangenome.
const base = 'https://jbrowse.org/demos/ecoli_pangenome'

const strains = [
  { name: 'K12', displayName: 'E. coli K12' },
  { name: 'Sakai', displayName: 'E. coli Sakai' },
  { name: 'CFT073', displayName: 'E. coli CFT073' },
  { name: 'NCTC86', displayName: 'E. coli NCTC86' },
]

const assemblies = strains.map(({ name, displayName }) => ({
  name,
  displayName,
  sequence: {
    type: 'ReferenceSequenceTrack',
    trackId: `${name}-ref`,
    // bare `uri` shorthand: the .fai/.gzi siblings are derived automatically
    adapter: { type: 'BgzipFastaAdapter', uri: `${base}/${name}.fa.gz` },
  },
}))

// A single all-vs-all PAF whose assemblyNames list every strain, so one track
// can align any adjacent pair — this one SyntenyTrack backs all three bands.
const tracks = [
  {
    type: 'SyntenyTrack',
    trackId: 'ecoli_ava',
    name: 'E. coli pangenome (all-vs-all PAF)',
    assemblyNames: strains.map(s => s.name),
    category: ['Synteny'],
    adapter: {
      type: 'AllVsAllPAFAdapter',
      pafLocation: { uri: `${base}/all_vs_all.paf.gz` },
      assemblyNames: strains.map(s => s.name),
    },
  },
]

export default function MultiwaySyntenyExample() {
  return (
    <JBrowse
      assemblies={assemblies}
      tracks={tracks}
      views={[
        {
          type: 'LinearSyntenyView',
          init: {
            // four strain rows → three bands, all backed by ecoli_ava
            views: strains.map(s => ({ assembly: s.name })),
            tracks: [['ecoli_ava'], ['ecoli_ava'], ['ecoli_ava']],
            drawCurves: true,
            // hide the short minimap2 alignments so the shared backbone reads
            // as clean ribbons instead of a dense noise band
            minAlignmentLength: 10000,
          },
        },
      ]}
    />
  )
}