Stone.jsDocs
Paradigm

Frontend

Navigation

Navigation runs on the same universal router as everything else. In the browser it moves between pages without a full reload; on the server it produces the first render. You write links and calls once, and they behave correctly in every rendering mode.

StoneLink is the link. In SPA and SSR it navigates client-side; where a real navigation is needed, it falls back to one. Give it a path or a generated URL.

app/pages/Nav.tsxtsx
import { StoneLink, useRouter, useRoute } from '@stone-js/use-react'

export function Nav () {
  const router = useRouter()
  const route = useRoute()
  return (
    <nav>
      <StoneLink to='/tasks' className={route?.name === 'tasks.list' ? 'active' : ''}>Tasks</StoneLink>
      <StoneLink to={router.generate({ name: 'tasks.show', params: { id: 1 } })}>First</StoneLink>
    </nav>
  )
}

#Programmatic navigation

When navigation follows an action, ask the router to navigate. Generate the target by name so the path stays in one place.

app/pages/NewTask.tsxtsx
const router = useRouter()

async function onSubmit (values: NewTask) {
  const task = await api.create(values)
  router.navigate(router.generate({ name: 'tasks.show', params: { id: task.id } }))
}

#Knowing where you are

useRoute() gives the active route, which is how you highlight the current link, read a param, or branch on the page. It updates as navigation happens.

#Where the navigation actually happens

Resolving a destination is the same everywhere: a route name plus params becomes a path. Performing it is not. The browser pushes a History entry and announces it, so the adapter hears the change and the kernel resolves the new page. A native application drives a navigation stack instead, and has no History API at all.

So only that last step is pluggable, under stone.router.navigator. You will not set it in a web application, where the browser navigator is already the default. A platform integration sets it once, and every navigate() call in your pages keeps working untouched: that is what lets the same pages run on another platform.

app/config.tsts
import { defineConfig } from '@stone-js/core'

export const AppConfig = defineConfig((blueprint) => {
  blueprint.set('stone.router.navigator', ({ path, replace, options }) => {
    // Perform the platform's effect. The path is already resolved.
  })
})

#Scroll restoration

Client navigation restores scroll position the way users expect: to the top on a new page, back to where they were on back/forward. Enable it once; it is not something you wire per link.

app/client.tsts
import { setupScrollRestoration } from '@stone-js/use-react'

setupScrollRestoration()   // top on navigate, remembered position on back/forward

#View transitions

Animate between pages with the View Transitions API, a cross-fade, a shared element, without hand-rolling animation state. It degrades gracefully where the API is absent and respects prefers-reduced-motion.

app/client.tsts
import { renderWithTransition } from '@stone-js/use-react'

// Wrap the render so route changes animate where the browser supports it.
renderWithTransition(app)

Stone.js

Your app exists in every runtime. Until you run it.

An open-source project by Stone Foundation
Created by Mr. Stone (Evens Pierre)