JBrowse 2 · Linear Genome View examples

Opening state & persistence

A richer initial view, then saving or sharing the session.

A view spelled out

View source — 42 lines
import { LinearGenomeView } from '@jbrowse/react-linear-genome-view2'

export default function WithInitAdvanced() {
  return (
    <LinearGenomeView
      assembly={{
        name: 'hg38',
        uri: 'https://jbrowse.org/genomes/GRCh38/fasta/hg38.prefix.fa.gz',
        refNameAliases: {
          uri: 'https://jbrowse.org/genomes/GRCh38/hg38_aliases.txt',
        },
        geneticCodes: { chrM: 2 },
      }}
      tracks={[
        {
          trackId: 'ncbi-refseq-genes',
          name: 'NCBI RefSeq Genes',
          uri: 'https://jbrowse.org/genomes/GRCh38/ncbi_refseq/GCA_000001405.15_GRCh38_full_analysis_set.refseq_annotation.sorted.gff.gz',
        },
      ]}
      height="400px"
      view={{
        loc: 'chr1:11,106,077-11,261,675',
        tracklist: true,
        nav: true,
        tracks: [
          { trackId: 'ncbi-refseq-genes', displaySnapshot: { height: 200 } },
        ],
        highlight: [
          'chr1:11,130,000-11,145,000',
          {
            refName: 'chr1',
            start: 11_200_000,
            end: 11_220_000,
            color: 'rgba(0, 128, 255, 0.25)',
            label: 'Region of interest',
          },
        ],
      }}
    />
  )
}

Persist & restore the session

The stored snapshot goes in session, not defaultSession, because its shape is only known at runtime, and it restores against the same assembly and tracks. The example removes the entry before using it, so a snapshot this build cannot open fails once rather than on every reload.

View source — 86 lines
import { useEffect } from 'react'

import { getSnapshot, onSnapshot } from '@jbrowse/mobx-state-tree'
import {
  JBrowseLinearGenomeView,
  createViewStateAsync,
  useCreateViewState,
} from '@jbrowse/react-linear-genome-view2'

const STORAGE_KEY = 'jbrowse-lgv-example-session'

const assembly = {
  name: 'volvox',
  uri: 'https://jbrowse.org/genomes/volvox/volvox.2bit',
}

const tracks = [
  {
    trackId: 'volvox_gff3',
    name: 'Volvox genes',
    uri: 'https://jbrowse.org/code/jb2/main/test_data/volvox/volvox.sort.gff3.gz',
  },
]

const freshSession = {
  name: 'Persisted session',
  view: {
    type: 'LinearGenomeView',
    assembly: 'volvox',
    loc: 'ctgA:1105..1221',
    tracks: ['volvox_gff3'],
  },
}

async function build() {
  const saved = localStorage.getItem(STORAGE_KEY)
  localStorage.removeItem(STORAGE_KEY)
  try {
    return await createViewStateAsync({
      assembly,
      tracks,
      session: saved ? JSON.parse(saved) : undefined,
      defaultSession: freshSession,
    })
  } catch (e) {
    console.error(e)
    return createViewStateAsync({
      assembly,
      tracks,
      defaultSession: freshSession,
    })
  }
}

export default function WithSessionPersistence() {
  const state = useCreateViewState(build)

  useEffect(() => {
    if (state) {
      const save = (snap: unknown) => {
        localStorage.setItem(STORAGE_KEY, JSON.stringify(snap))
      }
      save(getSnapshot(state.session))
      return onSnapshot(state.session, save)
    }
    return undefined
  }, [state])

  return state ? (
    <div>
      <p>
        Pan, zoom, or toggle tracks, then reload the page — the view comes back
        where you left it.{' '}
        <button
          onClick={() => {
            localStorage.removeItem(STORAGE_KEY)
            location.reload()
          }}
        >
          Reset saved session
        </button>
      </p>
      <JBrowseLinearGenomeView viewState={state} />
    </div>
  ) : null
}

Put the session in the URL

A decoded session goes in session, not defaultSession, which validates against a shape you wrote. The hash fragment never reaches the server, so a long session cannot fail with HTTP 414. Only the session travels: the receiving page supplies its own assembly and tracks.

View source — 97 lines
import { useState } from 'react'

import {
  JBrowseLinearGenomeView,
  createViewStateAsync,
  decodeSession,
  encodeSession,
  useCreateViewState,
} from '@jbrowse/react-linear-genome-view2'

const assembly = {
  name: 'volvox',
  uri: 'https://jbrowse.org/genomes/volvox/volvox.2bit',
}

const tracks = [
  {
    trackId: 'volvox_gff3',
    name: 'Volvox genes',
    uri: 'https://jbrowse.org/code/jb2/main/test_data/volvox/volvox.sort.gff3.gz',
  },
]

const freshSession = {
  name: 'Session in URL',
  view: {
    type: 'LinearGenomeView',
    assembly: 'volvox',
    loc: 'ctgA:1..50000',
    tracks: ['volvox_gff3'],
  },
}

function readSessionParam() {
  return (
    new URLSearchParams(window.location.hash.slice(1)).get('session') ??
    undefined
  )
}

function writeSessionParam(value: string) {
  const params = new URLSearchParams(window.location.hash.slice(1))
  params.set('session', value)
  window.history.replaceState(null, '', `#${params.toString()}`)
}

async function build(report: (status: string) => void) {
  const param = readSessionParam()
  if (param) {
    try {
      const session = await decodeSession(param)
      const engine = await createViewStateAsync({ assembly, tracks, session })
      report(`restored "${session.name}" from the URL`)
      return engine
    } catch (e) {
      console.error(e)
      report(`could not restore the session in the URL: ${e}`)
    }
  }
  return createViewStateAsync({
    assembly,
    tracks,
    defaultSession: freshSession,
  })
}

export default function SessionInUrl() {
  const [status, setStatus] = useState('')
  const state = useCreateViewState(() => build(setStatus))

  return state ? (
    <div>
      <div style={{ padding: 8, fontSize: 13, background: '#8881' }}>
        <button
          type="button"
          onClick={() => {
            void encodeSession(state)
              .then(encoded => {
                writeSessionParam(encoded)
                setStatus(
                  `saved to the URL (${encoded.length} chars) — copy the address bar, or reload to restore it`,
                )
              })
              .catch((e: unknown) => {
                console.error(e)
                setStatus(`could not save the session to the URL: ${e}`)
              })
          }}
        >
          Save this view to the URL
        </button>{' '}
        {status || 'navigate or toggle a track, then save'}
      </div>
      <JBrowseLinearGenomeView viewState={state} />
    </div>
  ) : null
}