All skills

Inertia.js server-side rendering, redirects, shared data, forms, validation, and client-side patterns for The Boring JavaScript Stack. Use this skill when building pages, handling form submissions, managing redirects, sharing data, or working with any Inertia.js feature in a Sails.js application.

Use this Skill: https://skilld.dev/gh/sailscastshq/boring-stack/inertia

This session only. Nothing lands on disk.

rulesrendering-pages.md

≈1.4k tokens on demand. Your agent reads this file only when SKILL.md points to it.

Rendering Pages

The Page Object

Every Inertia response is a "page object" -- a JSON structure that tells the client which component to render and what data to pass it:

{
  component: 'auth/login',       // Maps to assets/js/pages/auth/login.jsx
  url: '/login',                 // Current URL
  version: '1.0',               // Asset version (for cache busting)
  props: {                      // Data passed to the component
    passkeyChallenge: null,
    flash: {},
    errors: {}
  },
  deferredProps: {},             // Props loaded after initial render
  mergeProps: [],                // Props that merge instead of replace
  scrollRegions: [],             // Scroll position metadata
  encryptHistory: false,         // Whether to encrypt history state
  clearHistory: false            // Whether to clear history
}

Using responseType: 'inertia'

The most common pattern for GET routes. The action returns an object with page (component name) and optionally props:

Simple Page (No Props)

// api/controllers/home/view-home.js
module.exports = {
  exits: {
    success: { responseType: 'inertia' }
  },
  fn: async function () {
    return { page: 'index' }
  }
}

Page with Props

// api/controllers/auth/view-login.js
module.exports = {
  exits: {
    success: { responseType: 'inertia' }
  },
  fn: async function () {
    const passkeyChallenge = this.req.session.passkeyChallenge || null
    if (passkeyChallenge) delete this.req.session.passkeyChallenge
    return {
      page: 'auth/login',
      props: { passkeyChallenge }
    }
  }
}

Page with Data Fetching

// api/controllers/setting/view-team.js
module.exports = {
  inputs: {
    teamId: { type: 'number', required: true }
  },
  exits: {
    success: { responseType: 'inertia' },
    notFound: { responseType: 'notFound' }
  },
  fn: async function ({ teamId }) {
    var team = await Team.findOne({ id: teamId })
    if (!team) throw 'notFound'

    var memberships = await Membership.find({ team: team.id }).populate(
      'member'
    )
    var pendingInvites = await TeamInvite.find({
      team: team.id,
      status: 'pending'
    })

    return {
      page: 'settings/team',
      props: {
        team,
        memberships,
        pendingInvites
      }
    }
  }
}

How Rendering Works Internally

When sails.inertia.render(req, res, data) is called:

  1. Resolves shared props -- Merges global shared props (from sails.inertia.share()) with the action's page-specific props
  2. Handles partial reloads -- If the request has X-Inertia-Partial-Data or X-Inertia-Partial-Except headers, only the requested props are resolved
  3. Resolves deferred props -- Identifies DeferProp instances and separates them from immediate props
  4. Resolves merge/once props -- Processes MergeProp, OnceProp, and AlwaysProp instances
  5. Builds the page object -- Combines component name, URL, version, and resolved props
  6. Returns the response:
    • First visit (no X-Inertia header): Renders views/app.ejs with the page object in data-page
    • Subsequent visits (has X-Inertia header): Returns the page object as JSON with X-Inertia: true header

The page Property

The page string maps directly to a file path under assets/js/pages/:

page value File path
'index' assets/js/pages/index.jsx
'auth/login' assets/js/pages/auth/login.jsx
'settings/profile' assets/js/pages/settings/profile.jsx
'dashboard/view-dashboard' assets/js/pages/dashboard/view-dashboard.jsx

Overriding the Root View

By default, Inertia renders using views/app.ejs. You can override this per-request:

// Use a different root view for this request
sails.inertia.setRootView('auth') // Uses views/auth.ejs instead

This is useful for having different HTML shells (e.g., a minimal layout for auth pages).

Locals (Root Template Data)

Actions can return a locals object to pass data to the root EJS template (views/app.ejs). Locals are for the HTML shell -- <title>, <meta>, Open Graph tags -- not for your page components (use props for that).

return {
  page: 'courses/show',
  props: { course },
  locals: {
    title: course.title,
    description: course.description,
    ogImage: course.thumbnailUrl
  }
}

See locals.md for the full API, precedence rules, and real-world examples.

Rendering on Non-GET Routes

The inertia response type is almost always used with GET routes. For POST/PATCH/PUT/DELETE routes, you typically redirect after processing (see redirects-and-responses.md).

However, there are edge cases where a non-GET action renders a page:

// api/controllers/setting/delete-profile.js
module.exports = {
  exits: {
    success: { responseType: 'inertiaRedirect' },
    hasTeamMembers: { responseType: 'inertia' } // Show modal instead of redirecting
  },
  fn: async function () {
    // If user has team members, show a page instead of deleting
    if (hasMembers) {
      throw {
        hasTeamMembers: {
          page: 'settings/profile',
          props: { showTransferModal: true }
        }
      }
    }
    // ... proceed with deletion
  }
}

Source: SKILL.md on GitHub

3 warnings17d4 checks · Risk SAFE
  • Gen Agent Trust Hub17d

    This skill provides comprehensive documentation and guidelines for integrating Inertia.js within The Boring JavaScript Stack (Sails.js). No security vulnerabilities or malicious patterns were detected.

  • Socket17d

    1 alert: gptAnomaly

  • Snyk17d

    Risk: MEDIUM · 1 issue

  • Runlayer7mo

    16/16 files flagged

Signed by skilld at fb2b7e0. This ties the file your Agent reads to that commit on GitHub. It does not review the instructions.

Last checked against GitHub 3 days ago.

Activeupdated 7 months ago
Other metadata
metadata
{
  "author": "sailscastshq",
  "version": "1.0.0",
  "tags": "inertia, sails, boring-stack, spa, forms, redirects, shared-data, props"
}

README badge

README badge for sailscastshq/boring-stack/inertia