Stone.jsDocs
Paradigm

Frontend

Layouts

A layout is the frame around a page: the header, footer and shell that stay put while the page changes. Unlike a page, a layout is not an event handler; it lives purely in the view dimension, and it renders the page through a StoneOutlet.

#Defining a layout

Mark a class with @PageLayout({ name }) and render your shell, placing the page where StoneOutlet sits. A layout may implement head, but never handle: it does not process events.

app/layouts/BaseLayout.tsxdeclarativeimperative
import { PageLayout, IPageLayout, StoneOutlet } from '@stone-js/use-react'
import { ReactNode } from 'react'

@PageLayout({ name: 'default' })
export class BaseLayout implements IPageLayout {
  render ({ children }: { children: ReactNode }) {
    return (
      <div className='app'>
        <header>Tasks</header>
        <main><StoneOutlet>{children}</StoneOutlet></main>
        <footer>© Tasks</footer>
      </div>
    )
  }
}
import { definePageLayout, StoneOutlet } from '@stone-js/use-react'

const BaseLayout = () => ({
  render: ({ children }) => (
    <div className='app'>
      <header>Tasks</header>
      <main><StoneOutlet>{children}</StoneOutlet></main>
      <footer>© Tasks</footer>
    </div>
  )
})

export const layouts = [definePageLayout(BaseLayout, { name: 'default' }, true)]

#Named layouts

The name makes a layout selectable. The one named default wraps any page that does not choose otherwise; register others (admin, auth) and opt in per page.

app/pages/AdminPage.tsxdeclarativeimperative
@Page('/admin', { layout: 'admin' })
export class AdminPage implements IPage<ReactIncomingEvent> {
  render () { return <Dashboard /> }
}
const AdminPage = () => ({ render: () => <Dashboard /> })

export const pages = [definePage(AdminPage, { path: '/admin', layout: 'admin' }, true)]

#Root providers

Some context wraps the whole app, not one layout: a theme, an i18n provider, a data- fetching client. A view provider wraps the entire tree, above every layout and page, and runs on the server render and the client hydration alike, so context is consistent in both.

app/AppProviders.tsxdeclarativeimperative
import { ViewProvider, IViewProvider } from '@stone-js/use-react'
import { ReactNode } from 'react'

@ViewProvider()
export class AppProviders implements IViewProvider {
  render ({ children }: { children: ReactNode }) {
    return (
      <ThemeProvider>
        <QueryClientProvider client={queryClient}>
          {children}
        </QueryClientProvider>
      </ThemeProvider>
    )
  }
}
import { defineViewProvider } from '@stone-js/use-react'

const AppProviders = () => ({
  render: ({ children }) => (
    <ThemeProvider><QueryClientProvider client={queryClient}>{children}</QueryClientProvider></ThemeProvider>
  )
})

export const viewProviders = [defineViewProvider(AppProviders, {}, true)]

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)