PocketPages Datastar Backend SDK

Implements the Datastar SDK ADR for PocketPages, providing realtime DOM updates and signal management.

Installation

npm install pocketpages-plugin-datastar

Configuration

Add to your +config.js:

module.exports = {
  plugins: [
    'pocketpages-plugin-ejs',
    'pocketpages-plugin-datastar',
    'pocketpages-plugin-realtime',
  ],
}

Add to your +layout.ejs or similar:

<head>
  <%- datastar.scripts() %>
</head>

API Reference

Core Methods

datastar.patchElements(elements, options?)

Updates DOM elements with new HTML content.

// Basic usage
datastar.patchElements('<div>New content</div>')

// With options
datastar.patchElements('<div>New content</div>', {
  selector: '#target',
  mode: 'inner',
  useViewTransition: true,
  eventId: 'unique-id',
  retryDuration: 1000,
})

Options:

  • selector - CSS selector for target element
  • mode - Patch mode: outer, inner, remove, replace, prepend, append, before, after
  • useViewTransition - Enable ViewTransition API
  • eventId - Unique event identifier
  • retryDuration - SSE retry duration in milliseconds

datastar.patchSignals(signals, options?)

Updates client-side signals with new data.

// Basic usage
datastar.patchSignals(stringify({ count: 42 }))

// With options
datastar.patchSignals(stringify({ count: 42 }), {
  onlyIfMissing: true,
  eventId: 'unique-id',
  retryDuration: 1000,
})

Options:

  • onlyIfMissing - Only patch if signals don't exist
  • eventId - Unique event identifier
  • retryDuration - SSE retry duration in milliseconds

datastar.readSignals(request, target)

Reads signals from request and merges into target object.

// Read signals into a new object
const data = datastar.readSignals(request, {})

// Read into existing object
const form = datastar.readSignals(request, { name: '', email: '' })

Utility Methods

datastar.executeScript(script, options?)

Executes JavaScript on the client.

datastar.executeScript('console.log("Hello from server")')

// With options
datastar.executeScript('alert("Hello")', {
  autoRemove: true,
  attributes: ['type="module"'],
  eventId: 'script-1',
  retryDuration: 1000,
})

datastar.consoleLog(message, options?)

Logs a message to client console.

datastar.consoleLog('Server message')

datastar.consoleError(error, options?)

Logs an error to client console.

datastar.consoleError('Something went wrong')
datastar.consoleError(new Error('Server error'))

datastar.redirect(url, options?)

Redirects the client to a new URL.

datastar.redirect('/dashboard')

datastar.dispatchCustomEvent(eventName, detail, options?)

Dispatches a custom event on the client.

datastar.dispatchCustomEvent('user-updated', { id: 123 })

// With options
datastar.dispatchCustomEvent(
  'custom-event',
  { data: 'value' },
  {
    selector: '.target',
    bubbles: true,
    cancelable: true,
    composed: true,
  }
)

datastar.replaceURL(url, options?)

Updates the browser URL without navigation.

datastar.replaceURL('/new-path')

datastar.prefetch(urls, options?)

Prefetches URLs using speculation rules.

datastar.prefetch(['/page1', '/page2'])

Realtime Methods

datastar.realtime.patchElements(elements, patchOptions?, realtimeOptions?)

Broadcasts element updates to all connected clients.

// Basic usage
datastar.realtime.patchElements('<div>Broadcast message</div>')

// With patch options
datastar.realtime.patchElements('<div>Broadcast message</div>', {
  selector: '#target',
  mode: 'inner',
  useViewTransition: true,
})

// With realtime options for custom filtering
datastar.realtime.patchElements(
  '<div>Broadcast message</div>',
  {},
  {
    filter: (clientId, client, topic, message) => {
      // Only send to authenticated clients
      return client.get('auth')?.id
    },
  }
)

Parameters:

  • elements (string) - HTML content to broadcast
  • patchOptions (object, optional) - Element patch configuration
  • realtimeOptions (object, optional) - Realtime delivery options
    • filter (function, optional) - Custom filter function to target specific clients

datastar.realtime.patchSignals(signals, options?)

Broadcasts signal updates to all connected clients.

datastar.realtime.patchSignals(stringify({ globalCount: 100 }))

Examples

Chat Application

// Save message and broadcast to all clients
const messages = store('messages') || []
const { from, message } = request.url.query.datastar
messages.push({ from, message })
store('messages', messages)

// Broadcast updated chat box
datastar.realtime.patchElements(include('chat-box.ejs', { messages }))

// Clear input
datastar.patchSignals(stringify({ message: '' }))

Counter with Realtime Updates

// Increment counter
$app.runInTransaction(() => {
  store('count', (store('count') || 1) + 1)
})

// Return updated counter
<%- include('count.ejs') %>

Form Handling

// Read form data
const formData = datastar.readSignals(request, { name: '', email: '' })

// Process and respond
if (formData.name && formData.email) {
  datastar.patchElements('<div>Success!</div>')
} else {
  datastar.consoleError('Please fill all fields')
}

Client-Side Integration

The plugin automatically injects the Datastar loader script. Use DataStar attributes in your HTML:

<button data-on-click="@get('/api/increment')">
  Count: <span data-bind-count><%= store('count') %></span>
</button>

<form data-on-submit="@post('/api/submit')">
  <input name="name" data-bind-name />
  <button type="submit">Submit</button>
</form>

Client-Side Helper Functions

patchSignals(signals)

A client-side helper function that dispatches a datastar-fetch event to patch signals. This function is automatically available in the global scope when the datastar plugin is loaded.

// Patch signals from client-side JavaScript
patchSignals({ count: 42, message: 'Hello' })

// Patch signals with complex data
patchSignals({
  user: { id: 123, name: 'John' },
  settings: { theme: 'dark' },
})

This function is particularly useful for:

  • Updating signals from client-side event handlers
  • Syncing state between different parts of your application
  • Triggering signal updates from custom JavaScript code

$clientId Signal

The $clientId signal is automatically set when the realtime connection is established. This signal contains the unique client identifier assigned by PocketBase's realtime system.

<!-- Display the client ID -->
<div data-text="$clientId"></div>

<!-- Use in conditional rendering -->
<div data-if="$clientId">
  Connected with ID: <span data-text="$clientId"></span>
</div>

<!-- Use in data attributes -->
<button data-on-click="@get('/api/action')" data-client-id="$clientId">
  Perform Action
</button>

The $clientId is useful for:

  • Identifying the current client in realtime communications
  • Debugging connection issues
  • Creating client-specific functionality
  • Tracking user sessions

Event Types

  • datastar-patch-elements - DOM element updates
  • datastar-patch-signals - Signal updates

Default Values

  • DefaultSseRetryDuration: 1000ms
  • DefaultElementsUseViewTransitions: false
  • DefaultPatchSignalsOnlyIfMissing: false
  • DefaultElementPatchMode: 'outer'