Inactive mode stays normal
Without `?role=viewer` or `?role=presenter`, your route should look and behave like a normal Nuxt page.
Developer documentation
NPPT is a small API surface with a strong mental model: your normal routes stay normal, then presentation roles layer viewer and presenter behavior on top. This page is written for developers who want to integrate fast, reason about the runtime clearly and keep full control of their templates.
Why developers like it
No separate slide deck architecture
Routes, components and Tailwind classes stay in the app you already maintain.
Multi-page flow still feels native
Viewer and presenter roles follow route changes instead of forcing a special slideshow router.
Runtime control is optional
You can stay declarative, or drop down to `$nppt` methods when you need custom controls.
Quick start
If you only remember one thing, remember this flow: install the module, mount the presenter once, annotate the sections that matter, then simplify the viewer side where your public page is too dense for live delivery.
npm install @c1ach0/nppt
// nuxt.config.ts
export default defineNuxtConfig({
modules: ["@c1ach0/nppt"]
})<template>
<UApp>
<NuxtPage />
<NpptPresenter />
<NpptLauncher label="Start presentation" />
</UApp>
</template>Mental model
Without `?role=viewer` or `?role=presenter`, your route should look and behave like a normal Nuxt page.
The audience sees the page in its presentation shape: only the content you want to keep live, plus optional progress UI.
The speaker gets notes, keywords, timing, preview and step controls while staying on the same route structure.
Directive API
Attach `v-nppt` to the sections you want NPPT to recognize as focus points. Those sections become step-aware and can contribute titles, notes, navigation hints and presenter keywords.
| Field | Type | Status | Description |
|---|---|---|---|
| step | number | recommended | Order of the focus point inside the current route. |
| title | string | optional | Presenter-facing title for the active section. |
| note | string | optional | Speaker note shown only in presenter mode. |
| next | string | optional | Next route hint used by page-level navigation and presenter controls. |
| hideOn | 'presenter' | 'viewer' | optional | Suppresses the annotated step for one target while keeping it for the other. |
| keywords | string | NpptKeyword[] | optional | Talking points shown in the presenter dashboard. |
<section
v-nppt="{
step: 1,
title: 'Opening',
note: 'Set the context before diving into the product.',
keywords: [
{ label: 'Intro', tone: 'info', size: 'xl' },
{ label: 'Problem', tone: 'warning', size: 'lg' }
]
}"
>
<h1>Launch-ready analytics for fast-moving teams</h1>
<p>Use your real landing page as the first chapter of the talk.</p>
</section>v-nppt="{
step: 2,
title: 'Feature summary',
keywords: 'Viewer|info|xl,Presenter|success|lg,Sync|warning|md'
}"Components
none
Full presenter dashboard with timer, current title, note, keywords, route, viewer preview and controls.
label?: string
Floating trigger used before presentation starts. Launches the viewer window and turns the current page into presenter mode.
on?: 'viewer' | 'presenter' | 'any'
Renders content only while a presentation role is active.
on?: 'viewer' | 'presenter' | 'any'
Hides content only while a presentation role is active.
on?, fixed?, top?, right?, bottom?, left?, showLabel?
Simple progress bar mostly intended for the viewer side.
<NpptHideOnPresentation on="viewer">
<p>
This dense explanatory paragraph stays on the public site,
but disappears for the audience during the talk.
</p>
</NpptHideOnPresentation>
<NpptShowOnPresentation on="viewer">
<p>
This shorter version is easier to read from the back of the room.
</p>
</NpptShowOnPresentation><NpptProgress
on="viewer"
:fixed="true"
bottom="1rem"
left="1rem"
right="1rem"
:show-label="true"
/>Integration patterns
Best for product pitches, landing pages and technical explainers that stay on one URL. Annotate each major section and let steps move through the page.
Best for realistic website demos. Add `next` metadata on the last major beat of a route so presenter and viewer can move naturally to the next page.
<section
v-nppt="{
step: 4,
title: 'Portfolio handoff',
note: 'Move from the homepage promise to proof.',
next: '/portfolio'
}"
>
<NuxtLink to="/portfolio">
See selected work
</NuxtLink>
</section>If your team wants branded controls or internal tooling around the presentation flow, use the injected runtime API.
<script setup lang="ts">
const { $nppt } = useNuxtApp()
</script>
<template>
<div class="flex gap-2">
<button @click="$nppt.prev()">Prev</button>
<button @click="$nppt.next()">Next</button>
<button @click="$nppt.nextPage()">Next page</button>
</div>
</template>Best practices
Keep step granularity tied to speaking beats, not raw DOM chunks.
Use `title`, `note` and `keywords` on the sections that actually matter for the speaker.
Mount `NpptPresenter` globally so it survives route changes in a multi-page talk.
Treat `?role=viewer` and `?role=presenter` as presentation layers, never as the default public route.
Prefer progressive simplification for viewer mode instead of designing a completely different page.
Test the inactive route first, then test viewer mode, then presenter mode.
Runtime API
NPPT injects a runtime object into the Nuxt app and Vue component instance. That gives you imperative navigation and step control for custom buttons, shortcuts or internal presentation tools.
Reactive state surface
navigate(to, step?)
Change route and optionally set the active step.
next()
Move to the next step within the current route.
prev()
Move to the previous step within the current route.
nextPage()
Jump to the next route in the presentation flow.
prevPage()
Jump to the previous route in the presentation flow.
goTo(step)
Force the current step index.
resetFocus()
Refresh focus state for the current page.
launchPresentation()
Open the viewer window and switch the current tab to presenter mode.
<script setup lang="ts">
const { $nppt } = useNuxtApp()
async function openCaseStudy() {
await $nppt.navigate('/portfolio', 1)
}
function jumpToSummary() {
$nppt.goTo(3)
}
</script>
<template>
<button @click="openCaseStudy">
Open portfolio chapter
</button>
<button @click="jumpToSummary">
Jump to summary
</button>
</template>Troubleshooting
Check that you are using `NpptShowOnPresentation` and not a custom query-condition that accidentally defaults to visible.
Render `NpptPresenter` in `app.vue` or a global layout instead of a route component.
Add `next` metadata to the last meaningful step of the page and verify the target route exists in your app.
Move long explanations into `NpptHideOnPresentation` and add a smaller viewer-facing summary with `NpptShowOnPresentation`.