Synteny views
Compare assemblies with linear synteny — declaratively, via the imperative mount, or stacked multi-way.
On this page: Linear synteny 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 can hold any number of views of any type, side by side.
Each view type launches the same declarative way: a views
entry with a type and an init field. init is the same shape JBrowse Web
serializes into its ?session=spec-… URL parameter (see the
URL query parameter API), so these
examples are the programmatic equivalent of those URLs.
LinearSyntenyView shows two linear genome views with a connecting ribbon for
synteny features (PAF, MUMMER, etc.). The init field declares the two member
assemblies and the synteny track that ties them together, much terser than
hand-building two LinearGenomeView snapshots plus a synteny view snapshot:
{
type: 'LinearSyntenyView',
init: {
views: [{ assembly: 'volvox' }, { assembly: 'volvox_del' }],
tracks: ['volvox_del.paf'],
},
}
The exact fields each view’s init/snapshot accepts come from its
state model docs. See
LinearSyntenyView.
View source
import { JBrowse } from '@jbrowse/react-app2'
import { volvoxConfig } from '../volvoxConfig.ts'
// the volvox config includes both 'volvox' and 'volvox_del' assemblies and a
// 'volvox_del.paf' synteny track
export default function SyntenyExample() {
return (
<JBrowse
assemblies={volvoxConfig.assemblies}
tracks={volvoxConfig.tracks}
views={[
{
type: 'LinearSyntenyView',
init: {
views: [{ assembly: 'volvox' }, { assembly: 'volvox_del' }],
tracks: ['volvox_del.paf'],
},
},
]}
/>
)
}Synteny via the imperative mount
Mount the full app with createApp() — the framework-agnostic primitive non-React hosts (anywidget, htmlwidgets) use — and open a synteny view declaratively.
Everything else on this site drives the <JBrowse> React
component. The same package exposes the same engine a different way:
createApp(element, options), a framework-agnostic imperative mount with no
React in its signature. It’s the multi-view counterpart to
@jbrowse/react-linear-genome-view2’s createLinearGenomeView, and the
primitive that non-React hosts (Jupyter anywidgets, R htmlwidgets, plain
<script> pages) wrap. You still install react and react-dom as peers.
createApp saves you writing JSX and managing a React root, not React itself.
Because it drives the full app, one declarative views list reaches every view
type. Here a LinearSyntenyView, the exact same { type, init } shape the
<JBrowse> synteny example uses:
import '@jbrowse/react-app2/styles.css'
import { createApp } from '@jbrowse/react-app2'
const controller = createApp(document.getElementById('root'), {
assemblies,
tracks,
views: [
{
type: 'LinearSyntenyView',
init: {
views: [{ assembly: 'volvox' }, { assembly: 'volvox_del' }],
tracks: ['volvox_del.paf'],
},
},
],
})
// later: controller.addView({ type: 'DotplotView', init: {...} })
// on teardown: 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: it’s a plain,
self-contained CSS file.
The init field is the same vocabulary JBrowse Web serializes into its
?session=spec-… URLs, so anything expressible there (synteny, dotplot,
circular, breakpoint-split) is one entry in views.
View source
import { createApp } from '@jbrowse/react-app2'
import { volvoxConfig } from '../volvoxConfig.ts'
// 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: volvoxConfig.assemblies,
tracks: volvoxConfig.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 the view stacks them as rows, each adjacent pair joined by a
connecting ribbon. This example stacks 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 band: tracks[i]
holds the track(s) connecting views[i] and views[i+1]. Because a single
all-vs-all PAF aligns every strain to every other, one
SyntenyTrack using an
AllVsAllPAFAdapter
(whose assemblyNames lists all four strains) backs all three bands:
{
type: 'LinearSyntenyView',
init: {
views: [
{ assembly: 'K12' },
{ assembly: 'Sakai' },
{ assembly: 'CFT073' },
{ assembly: 'NCTC86' },
],
tracks: [['ecoli_ava'], ['ecoli_ava'], ['ecoli_ava']],
drawCurves: true,
minAlignmentLength: 10000,
},
}
minAlignmentLength hides the short minimap2 alignments so the shared backbone
reads as clean ribbons. The nested tracks shape matches JBrowse Web’s
multi-way ?session=spec-… URL. See the multi-way section of the
URL query parameter API. For separate
pairwise files instead of one all-vs-all track, give each band its own PAF.
The full set of fields init accepts is in the
LinearSyntenyView state model docs.
View source
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,
},
},
]}
/>
)
}