• Townie
    AI
  • Blog
  • Docs
  • Pricing
  • We’re hiring!
Log inSign up
zcpbx

zcpbx

MolstarViewer

Public
Like
MolstarViewer
Home
Code
7
backend
2
frontend
7
.vtignore
AGENTS.md
FIXES.md
README.md
deno.json
Branches
1
Pull requests
Remixes
History
Environment variables
Val Town is a collaborative website to build and scale JavaScript apps.
Deploy APIs, crons, & store data – all from the browser, and deployed in milliseconds.
Sign up now
Code
/
README.md
Code
/
README.md
Search
…
Viewing readonly version of main branch: v627
View latest version
README.md

Molstar Viewer Apps

A single-page application (SPA) for molecular visualization using Molstar, built for Val Town with client-side routing.

πŸš€ Quick Start

This Val serves a React SPA with four different Molstar visualization applications:

  • Home - Landing page with app navigation
  • Viewer 🧬 - Standard Mol* viewer for visualizing molecular structures
  • Docking Viewer πŸ”¬ - Specialized viewer for molecular docking results
  • Mesoscale Explorer πŸ”­ - Explore mesoscale biological structures
  • MVS Stories πŸ“– - Interactive molecular visualization stories

πŸ“ Project Structure

Simple SPA structure with all frontend code in one place:

MolstarViewer/
β”œβ”€β”€ index.html                   # HTML entry point
β”œβ”€β”€ index.http.tsx               # Server: serves static files (HTTP handler)
└── frontend/                    # All client-side code
β”œβ”€β”€ index.tsx               # Client entry: mounts React app
β”œβ”€β”€ components/             # React components
β”‚   β”œβ”€β”€ App.tsx            # Main app with client-side routing
β”‚   β”œβ”€β”€ ErrorBoundary.tsx  # Error handling component
β”‚   └── MolstarViewer.tsx  # Reusable Molstar viewer component
β”œβ”€β”€ viewer/                 # Viewer-specific modules (if needed)
β”œβ”€β”€ docking-viewer/         # Docking-specific modules (if needed)
β”œβ”€β”€ mesoscale-explorer/     # Mesoscale-specific modules (if needed)
└── styles/                 # CSS files
    └── molstar.css         # Compiled Molstar styles

## πŸ› οΈ Technology Stack

- **Runtime**: Val Town (Deno-based)
- **Server**: Hono v4 from JSR (static file serving only)
- **UI**: React 18.2.0 via esm.sh (client-side SPA)
- **Routing**: Custom client-side routing (History API)
- **Molstar**: JSR package `jsr:@zachcp/molstar@5.3.17`
- **Language**: TypeScript/TSX

## πŸ—οΈ Architecture

### Server (index.http.tsx)

The server is extremely simple - it just serves static files:

```typescript
import { Hono } from "jsr:@hono/hono@4";
import { serveFile } from "https://esm.town/v/std/utils@85-main/index.ts";

const app = new Hono();

// Serve all frontend files
app.get("/frontend/*", (c) => serveFile(c.req.path, import.meta.url));

// Serve the root HTML file for all routes (SPA)
app.get("*", (c) => serveFile("/index.html", import.meta.url));

export default app.fetch;

Client Entry (frontend/index.tsx)

Mounts the React app to the DOM:

import { App } from "/frontend/components/App.tsx"; import { ErrorBoundary } from "/frontend/components/ErrorBoundary.tsx"; import { createRoot } from "https://esm.sh/react-dom@18.2.0/client"; import React from "https://esm.sh/react@18.2.0"; const root = createRoot(document.getElementById("root")!); root.render( <React.StrictMode> <ErrorBoundary> <App /> </ErrorBoundary> </React.StrictMode>, );

App Component (frontend/components/App.tsx)

Handles client-side routing using the History API:

export function App() { const [currentPath, setCurrentPath] = useState(window.location.pathname); const navigate = (path: string) => { window.history.pushState({}, "", path); setCurrentPath(path); }; // Render different views based on currentPath if (currentPath === "/viewer") { return <MolstarViewer />; } // ... etc }

MolstarViewer Component

Reusable component that dynamically imports and initializes Molstar:

export function MolstarViewer({ layoutIsExpanded, layoutShowControls }) { const containerRef = useRef<HTMLDivElement>(null); useEffect(() => { const { Viewer } = await import("jsr:@zachcp/molstar@5.3.17"); const viewer = await Viewer.create(containerRef.current, { layoutIsExpanded, layoutShowControls, }); }, []); return <div ref={containerRef} />; }

πŸ“¦ JSR Imports

Molstar from JSR

import { Viewer } from "jsr:@zachcp/molstar@5.3.17";

Available exports from jsr:@zachcp/molstar:

  • . (main export - includes Viewer class)
  • ./plugin-ui
  • ./plugin
  • ./plugin-state
  • ./canvas3d
  • ./extensions

Hono from JSR

import { Hono } from "jsr:@hono/hono@4";

Why JSR?

JSR (JavaScript Registry) provides native Deno/TypeScript support and works seamlessly in Val Town's runtime environment.

πŸ”§ Development

Current Status

  • βœ… HTTP entry point properly named (index.http.tsx)
  • βœ… Single-page application with client-side routing
  • βœ… Hono server serves static files only
  • βœ… React app handles all routing client-side
  • βœ… Reusable MolstarViewer component
  • βœ… Dynamic import of Molstar (code splitting)
  • βœ… Error boundary for error handling
  • βœ… All frontend code in frontend/ folder
  • βœ… Home page with navigation
  • βœ… Viewer app fully functional
  • βœ… Docking viewer scaffolded
  • βœ… Mesoscale explorer scaffolded
  • βœ… MVS Stories scaffolded

Key Implementation Details

  1. SPA Architecture: Server returns index.html for all routes, React handles routing
  2. Client-Side Routing: Uses History API (pushState, popstate)
  3. Dynamic Imports: Molstar is loaded dynamically in useEffect for code splitting
  4. No Server-Side Rendering: Pure client-side React app
  5. Static File Serving: Server just serves files from frontend/ directory
  6. Error Handling: ErrorBoundary catches React errors, console catches runtime errors

Next Steps

  1. Add URL parameters for loading specific structures (e.g., /viewer?pdb=1ABC)
  2. Implement app-specific features:
    • Viewer: Load PDB structures by ID
    • Docking Viewer: Load and compare docking results
    • Mesoscale Explorer: Load mesoscale structures
    • MVS Stories: Story navigation and playback
  3. Add loading indicators while Molstar initializes
  4. Optimize bundle size with better code splitting
  5. Add keyboard shortcuts and controls

πŸ“š Resources

  • Mol* Website
  • Val Town Documentation
  • JSR Package Registry
  • Hono Framework

πŸ“ Notes

Important Patterns

  • Server is a simple static file server - no server-side routes
  • All routing happens client-side using History API
  • Molstar is imported dynamically in the browser
  • JSR imports work perfectly in client-side <script type="module"> tags
  • No build step required - TypeScript runs directly in browser via esm.sh

Val Town Specifics

  • Single index.http.tsx at root serves as HTTP handler
  • All frontend code in frontend/ directory
  • Server uses serveFile utility to serve static files
  • Client imports use absolute paths starting with /frontend/
  • JSR imports work seamlessly in Val Town's Deno runtime

Client-Side Routing

  • Uses window.history.pushState() for navigation
  • Listens to popstate event for back/forward buttons
  • Server returns index.html for all routes (catch-all)
  • React re-renders based on currentPath state

🀝 Contributing

When adding new features:

  1. New Routes: Add new route logic to App.tsx component
  2. New Components: Create in frontend/components/
  3. Shared Code: Put in frontend/ for easy importing
  4. Use JSR Imports: For Molstar and other Deno-compatible packages
  5. Follow Val Town Best Practices: See AGENTS.md

Example adding a new route:

// In App.tsx if (currentPath === "/my-new-page") { return ( <div> <h1>My New Page</h1> <MolstarViewer /> </div> ); }

πŸ“„ License

Copyright (c) 2018-2025 mol* contributors, licensed under MIT

FeaturesVersion controlCode intelligenceCLI
Use cases
TeamsAI agentsSlackGTM
DocsShowcaseTemplatesNewestTrendingAPI examplesNPM packages
PricingNewsletterBlogAboutCareers
We’re hiring!
Brandhi@val.townStatus
X (Twitter)
Discord community
GitHub discussions
YouTube channel
Bluesky
Open Source Pledge
Terms of usePrivacy policyAbuse contact
Β© 2025 Val Town, Inc.