API Reference

Complete API documentation for prestruct hooks and utilities.

usePageMeta

Keeps head tags in sync during client-side navigation.

Signature

usePageMeta({
  title,
  description,
  path,
  siteUrl
})

Parameters

Parameter Type Required Description
title string Yes Page title (used for <title>, og:title, twitter:title)
description string No Meta description (used for meta description, og:description, twitter:description)
path string Yes Route path for canonical URL generation
siteUrl string No Full site URL. Defaults to import.meta.env.VITE_SITE_URL

Usage

import usePageMeta from '../hooks/usePageMeta.js'

export default function About() {
  usePageMeta({
    siteUrl: 'https://example.com',
    path: '/about/',
    title: 'About | Example',
    description: 'Learn more about our mission.',
  })
  
  return <div>About page content</div>
}

Tags Updated

This hook updates these tags on navigation:

  • <title>
  • <meta name="description">
  • <link rel="canonical">
  • <meta property="og:url">
  • <meta property="og:title">
  • <meta property="og:description">
  • <meta name="twitter:title">
  • <meta name="twitter:description">

These tags remain unchanged (set once at load):

  • og:image, og:type, og:locale, og:site_name
  • twitter:card, twitter:image

Tip: Wrapper Hook

Avoid repeating siteUrl in every page:

// src/hooks/useMeta.js
import usePageMeta from './usePageMeta.js'

const SITE_URL = import.meta.env.VITE_SITE_URL || 'https://example.com'

export default function useMeta(config) {
  return usePageMeta({
    siteUrl: SITE_URL,
    ...config
  })
}

// Now in pages:
import useMeta from '../hooks/useMeta.js'

function About() {
  useMeta({
    path: '/about/',
    title: 'About | Example',
    description: '...',
  })
}

mountIslands

Mounts dynamic React components into <pre-island> placeholders.

Signature

mountIslands(registry)

Parameters

Parameter Type Required Description
registry object Yes Map of island names to React components

Usage

import { mountIslands } from './islands.js'
import { islands } from './AppIslands.jsx'

// In AppLayout.jsx - call after each navigation
useEffect(() => {
  const timer = setTimeout(() => mountIslands(islands), 0)
  return () => clearTimeout(timer)
}, [pathname])

Registry Format

// src/AppIslands.jsx
import RecentlyViewed from './islands/RecentlyViewed.jsx'
import CartWidget from './islands/CartWidget.jsx'

export const islands = {
  'recently-viewed': RecentlyViewed,
  'cart-widget': CartWidget,
}

pre-island Element

Custom element for declaring dynamic content.

Attributes

Attribute Values Default Description
data-pre-island string Required Island name (must match registry key)
data-pre-load eager | visible | idle eager Load strategy

Examples

// Mounts immediately after hydration
<pre-island data-pre-island="recently-viewed" />

// Mounts when scrolled into view
<pre-island data-pre-island="cart-widget" data-pre-load="visible">
  <span className="loading">Loading cart...</span>
</pre-island>

// Mounts during browser idle time
<pre-island data-pre-island="promo-banner" data-pre-load="idle" />

Fallback Content

Content inside <pre-island> renders in the static HTML and is visible to crawlers:

<pre-island data-pre-island="cart-widget">
  <div className="cart-skeleton">
    <div className="skeleton-line"></div>
    <div className="skeleton-line"></div>
  </div>
</pre-island>

This shows a skeleton while the React component loads.


Load Strategies

eager (default)

Mounts immediately after mountIslands() is called.

<pre-island data-pre-island="header-cart" />

visible

Mounts when the element enters the viewport using IntersectionObserver.

<pre-island data-pre-island="footer-newsletter" data-pre-load="visible" />

idle

Mounts during browser idle time using requestIdleCallback. Falls back to setTimeout(200) if unavailable.

<pre-island data-pre-island="chat-widget" data-pre-load="idle" />

Island Components

Props

Island components receive no props by default. To pass data:

Option 1: Read from element

// HTML
<pre-island data-pre-island="product-card" data-product-id="123" />

// Component
export default function ProductCard() {
  const el = document.currentScript?.previousElementSibling
  const productId = el?.dataset.productId
  
  // Fetch data using productId
}

Option 2: Use global state/localStorage

export default function CartWidget() {
  const [items, setItems] = useState([])
  
  useEffect(() => {
    // Read from your state management or localStorage
    const cart = JSON.parse(localStorage.getItem('cart') || '[]')
    setItems(cart)
  }, [])
  
  return <div>{items.length} items in cart</div>
}

ssr.config.js

Required Fields

export default {
  siteUrl: 'https://example.com',  // No trailing slash
  appLayoutPath: '/src/AppLayout.jsx',
  routes: [
    {
      path: '/',
      meta: { title: 'Home', description: '...' }
    }
  ]
}

Optional Fields

export default {
  // Site identity
  siteName: 'Example',
  author: 'Your Name',
  tagline: 'Your tagline',
  ogImage: '/og-default.png',
  keywords: 'keyword1, keyword2',
  
  // Routes
  routes: [
    {
      path: '/',
      priority: '1.0',         // Sitemap priority (string!)
      changefreq: 'weekly',   // Sitemap change frequency
      meta: {
        title: 'Home | Example',
        description: '...',
        ogImage: '/custom-og.png',  // Optional override
        noindex: false,             // Add noindex meta
        canonical: '/',             // Explicit canonical URL
      }
    }
  ],
  
  // JSON-LD structured data
  buildJsonLd() {
    return [
      {
        '@context': 'https://schema.org',
        '@type': 'Organization',
        name: 'Example',
        url: 'https://example.com'
      }
    ]
  },
  
  // Proxy configuration (optional)
  proxy: {
    url: 'https://proxy.example.com',
    targetUrl: 'https://origin.example.com',
    secret: 'your-secret',
    botList: ['googlebot', 'bingbot']
  }
}

Environment Variables

Build Time

Variable Description
VITE_SITE_URL Production URL (used by usePageMeta)
NODE_ENV Set to production for builds

Runtime (Cloudflare)

Variable Description
PRODUCTION Set to true for production
PRESTRUCT_SECRET Auth secret for proxy cache refresh
PRESTRUCT_TARGET_URL Target URL for proxy