vlt / affordances
  • Quickstart
  • Concepts
  • Guides
  • Reference
  • Overview
  • Getting started
  • Files and conventions
  • Routing
  • Route handlers
  • Plugins
  • Sessions
  • How they combine
  • Write a plugin
  1. Guides
  2. /
  3. Next.js
  4. /
  5. Plugins

Plugins

Add more capabilities to the same Next.js config through withAffordances's plugins, and write your own.

A plugin adds a capability to an Affordances app, such as serving every page as Markdown, without another config wrapper to order by hand. Plugins in Concepts explains what they are, and the Plugins section lists each one. Pass plugins to withAffordances:

next.config.ts
import { withAffordances } from '@affordances/next/config'
import { md } from '@affordances/md/next'

export default withAffordances(nextConfig, { name: 'Store', plugins: [md()] })

Then add each plugin's route handler. For md():

Terminal
vlx @affordances/md initnpx @affordances/md initpnpm dlx @affordances/md inityarn dlx @affordances/md initbunx @affordances/md initdeno run -A npm:@affordances/md init

This site runs with md(): add .md to any page's URL. See Markdown.

How they combine

  • Plugins run in order, after the graph's own rewrites and headers.
  • Each plugin owns a route handler under /api/affordances/<name>.
  • Each one matches its own requests, such as Accept: text/markdown for md(), so plugins and the graph never answer for each other.

Write a plugin

A plugin is a config wrapper: it takes the Next config and returns it, the same shape as createMDX(). Call the config's own rewrites and headers so you keep what came before:

lib/well-known.ts
import type { AffordancesPlugin } from '@affordances/next/config'

// serves /.well-known/agents.json from a route handler at /api/affordances/agents
export const agents = (): AffordancesPlugin => (config) => ({
  ...config,
  rewrites: async () => {
    const before = await config.rewrites?.()
    const rule = { source: '/.well-known/agents.json', destination: '/api/affordances/agents' }
    return Array.isArray(before)
      ? [rule, ...before]
      : { ...before, beforeFiles: [rule, ...(before?.beforeFiles ?? [])] }
  },
})
next.config.ts
export default withAffordances(nextConfig, { plugins: [md(), agents()] })

Keep its route under /api/affordances/<name>, and match only the requests it's for: withAffordances already claims the Affordances media type and ?affordances=1.


PreviousRoute handlersNextSessions

On this page

  • How they combine
  • Write a plugin
Edit this page

Make your app agent-operable

Give every page a graph and every server action an agent path: HTTP, WebMCP, and MCP, with the same session and the same checks. One command sets it up.

Read the quickstart