@somnia-chain/explorer-sidebar

Explorer sidebar

The Somnia Explorer's sidebar, chain picker, top bar and shell, as a package. Today the explorer renders it around every page. When each product moves to its own repo, each app renders the same shell, so the explorer still looks and navigates like one product.

Live demo

Below, this site plays the Oracle zone. Oracle links and the tabs route inside this app; every other product opens the live explorer. Narrow the window to see the mobile drawer.

Open the demo full screen →

Built to be embedded

Own CSSShips precompiled styles.css. Host apps don't need Tailwind or any particular config.
No collisionsEvery class carries the ses: prefix and there is no global reset; tokens are scoped to the sidebar's own elements.
Same themeFollows data-theme on <html> and toggles it with the same storage key as @somnia-chain/ui.
Same chainThe selected chain is stored under somnia-chain, so it carries across zones on one origin and syncs between tabs. Pass chainHref to carry the picker's chain in links to the explorer's own pages (Blocks, Tokens, Charts and the rest), so following one stays on it.
Zone-aware linksLinks into the host app use its router; links to other products are plain links to the explorer.
One product listNAV_ITEMS and NAV_SECTIONS name every product as on the platform site, with ledes and sub-pages for page headers.

How links resolve

Setup/oracle/templates/agents
Monolith (no zone)<Link href="/oracle/templates"><Link href="/agents">
Oracle zone, with ZoneLink<Link href="/oracle/templates"><a href="/agents">
Oracle zone, zone and explorerUrl props<Link href="/oracle/templates"><a href="{explorerUrl}/agents">

Usage

The explorer: the default app

app-shell.tsx
tsx
"use client"
// apps/explorer/components/shell/app-shell.tsx: the explorer's default app
import { type LinkComponent, ExplorerShell } from "@somnia-chain/explorer-sidebar"
import { ZoneLink } from "@somnia-chain/zones"
import { usePathname } from "next/navigation"

export function AppShell({ children }: { children: React.ReactNode }) {
  return (
    <ExplorerShell
      sidebar={{ currentPath: usePathname(), linkAs: ZoneLink as LinkComponent }}
      topbar={{ actions: <ConnectWallet /> }}
    >
      {children}
    </ExplorerShell>
  )
}

A product in its own repo

The explorer is Next.js multi-zones: its default app rewrites /oracle/* to the Oracle app, which forwards everything else back. See apps/explorer/ZONES.md.

oracle app
tsx
// A product app: a zone of the explorer (see somnia-platform-product-template).
// Pages live at their full paths, e.g. app/oracle/**.

// next.config.ts
import { withZones } from "@somnia-chain/zones/next"
import zones from "./zones.json" // a copy of the explorer's zones.json
export default withZones({}, { config: zones, zone: "somnia-oracle" })

// app/layout.tsx
import "@somnia-chain/explorer-sidebar/styles.css"
import "./globals.css" // @import "tailwindcss"; @import "@somnia-chain/ui/theme.css";

// components/shell.tsx
"use client"
import { type LinkComponent, ExplorerShell } from "@somnia-chain/explorer-sidebar"
import { ZoneLink } from "@somnia-chain/zones"
import { usePathname } from "next/navigation"

export function Shell({ children }: { children: React.ReactNode }) {
  return (
    // ZoneLink knows which zone serves each path:
    // /oracle/* navigates client-side, /agents loads the explorer.
    <ExplorerShell sidebar={{ currentPath: usePathname(), linkAs: ZoneLink as LinkComponent }}>
      {children}
    </ExplorerShell>
  )
}

The sidebar alone, in any React app

layout.tsx
tsx
import { ChainProvider, ExplorerSidebar, useChain } from "@somnia-chain/explorer-sidebar"
import "@somnia-chain/explorer-sidebar/styles.css"

<ChainProvider>
  <div className="flex">
    <ExplorerSidebar currentPath={location.pathname} zone={["/agents"]} explorerUrl="https://platform.somnia.network" />
    <YourApp />  {/* useChain() gives the chain picked in the sidebar */}
  </div>
</ChainProvider>

Props

ExplorerShell

PropTypeDefaultDescription
sidebarExplorerSidebarProps—Everything above except open and onClose.
topbar{ search?, status?, actions?, themeToggle? } | false{}Top bar slots; false hides it.
footerReactNode | false<ExplorerFooter />Replaces or hides the footer.

ExplorerSidebar

PropTypeDefaultDescription
currentPathstring—The router's pathname, without basePath. Drives the active entry.
zonestring[]every routeExplorer path prefixes this app serves. Links outside load the explorer.
explorerUrlstring""Origin for links outside the zone. Empty for same-origin zones.
basePathstring—This app's Next basePath; stripped from hrefs given to linkAs.
linkAsLinkComponent<a>Router link for routes inside the zone. With @somnia-chain/zones, pass ZoneLink and skip zone and explorerUrl.
chainHref(href, chainId) => string—How a link to one of the explorer's own pages names the picker's chain, in the app's own URL scheme. Without it those links stay as the registry has them.
sectionsNavSection[]NAV_SECTIONSNavigation tree; defaults to every Somnia product.
chainSelectorbooleantrueShow the chain picker.
footerLinks{ href, label }[]Docs, PlatformExternal links at the bottom.
badgestring"Explorer"Label beside the logo.
open / onCloseboolean / () => void—Mobile drawer state. ExplorerShell manages it for you.

Also exported

ExplorerTopbar, ExplorerSearch, ExplorerThemeToggle, ExplorerFooterThe shell's parts, for custom layouts.
ChainProvider, useChain, CHAINS, latencyTone, fmtLatencyThe selected chain and round-trip latencies from the browser.
NAV_ITEMS, NAV_SECTIONS, productFor, sectionOfThe product registry: names, icons, ledes, sub-pages, status.
ExplorerLink, ExplorerRoutingZone-aware link, for your own cross-product links.