Skip to Content
Newnadoon AI FrameworkBring agent to Frontend apps

Bring Agents to Frontend Apps

Newnadoon lets you bring your agent directly into a frontend application so users can talk to it through a chat experience.

The simplest mental model is this:

  • your agent runs on the server
  • your frontend sends tasks to that agent through one API route
  • the UI listens for events and streamed output
  • the user sees a live response in the browser

If you are building with Next.js App Router, the framework already gives you the main pieces:

  • createNewnadoonApiRoute() for the server route
  • useNewnadoonAgent() for direct frontend control
  • ready-made chat hooks and UI components for common layouts

What this page covers

This page shows how to:

  1. connect your Next.js app to a Newnadoon agent
  2. expose the runtime through a single App Router API route
  3. use the agent in four frontend patterns: full-page chat, embedded panel, floating widget, and fully custom UI with useNewnadoonAgent()

How the frontend flow works

When a user types a message in your app:

  1. the frontend sends a request to /api/newnadoon/run
  2. Newnadoon starts the selected agent
  3. the frontend subscribes to /api/newnadoon/events
  4. the UI receives tokens, state changes, and final results
  5. the answer is rendered back into your chat experience

This is why the route helper and the React hook work together.

Before you start

Make sure you already have:

  • a registered agent in newnadoon.json
  • a Next.js App Router application
  • an agent file that exports a default Newnadoon agent

Example newnadoon.json:

{ "agents": { "general-department-agent": { "name": "General Department Agent", "path": "./agent.ts" }, "customer-success-department-agent": { "name": "Department Routing Example Agent", "path": "./agent-customer-success.ts" } } }

1. Add the API route

The frontend needs a backend route that can:

  • start agent runs
  • stream runtime events
  • list registered agents
  • return graph information

Newnadoon gives you a single helper for that.

Create this file:

app/api/newnadoon/[...path]/route.ts

import { createNewnadoonApiRoute } from '@newnadoon/ai-framework/next/app-router' export const runtime = 'nodejs' export const { GET, POST } = createNewnadoonApiRoute()

That one file handles the core runtime endpoints under /api/newnadoon/*.

In simple terms: this route is the bridge between your frontend and your agent runtime.

2. Choose which agent the frontend should use

Most frontend apps use an agent id from environment variables so you can switch agents without changing UI code.

Example:

NEXT_PUBLIC_AGENT_ID=general-department-agent

Then in your page or component:

const agentId = process.env.NEXT_PUBLIC_AGENT_ID || 'general-department-agent'

This keeps the frontend simple while letting the backend load the correct registered agent.

3. Use the prebuilt full-page chat

Use this when the whole page is dedicated to the agent.

This is the quickest option if you want a complete chat screen with very little code.

import { FullPageAgentChat } from '@newnadoon/ai-framework/react' const agentId = process.env.NEXT_PUBLIC_AGENT_ID || 'general-department-agent' export default function FullPageExamplePage() { return <FullPageAgentChat agentId={agentId} showDebugPanel /> }

Use this pattern for:

  • support copilots
  • internal assistant pages
  • workflow command centers
  • dedicated AI workspaces inside your app

4. Use the embedded panel chat

Use this when the agent should live inside part of an existing page instead of taking over the whole screen.

import { PanelAgentChat } from '@newnadoon/ai-framework/react' const agentId = process.env.NEXT_PUBLIC_AGENT_ID || 'general-department-agent' export default function PanelExamplePage() { return ( <main style={{ padding: 24 }}> <h1>Panel Integration</h1> <PanelAgentChat agentId={agentId} showDebugPanel /> </main> ) }

Use this pattern for:

  • dashboards
  • admin tools
  • side panels
  • CRM or ops screens where AI is one part of the page

5. Use the floating widget chat

Use this when you want the agent to be available everywhere in the app as an on-demand assistant.

import { BubbleWidgetAgentChat } from '@newnadoon/ai-framework/react' const agentId = process.env.NEXT_PUBLIC_AGENT_ID || 'general-department-agent' export default function WidgetExamplePage() { return ( <main style={{ minHeight: '70vh', padding: 24 }}> <h1>Bubble Widget Integration</h1> <BubbleWidgetAgentChat agentId={agentId} showDebugPanel /> </main> ) }

Use this pattern for:

  • website assistants
  • product help widgets
  • internal floating copilots
  • quick access agent actions without changing the main layout

6. Build a custom frontend with useNewnadoonAgent()

If you want full control over the UI, use useNewnadoonAgent() directly.

This is the lowest-level frontend hook and the best choice when you want to design your own experience rather than use the built-in chat layouts.

'use client' import { FormEvent, useMemo, useState } from 'react' import { useNewnadoonAgent } from '@newnadoon/ai-framework/react' export default function RawHookAgentPanel({ agentId }: { agentId: string }) { const [task, setTask] = useState('') const { run, status, isRunning, error, tokens, finalState, reset } = useNewnadoonAgent({ agentId, runUrl: '/api/newnadoon/run', eventsUrl: '/api/newnadoon/events', autoConnectEvents: true }) const answer = useMemo(() => { if (tokens.trim()) return tokens return String((finalState as any)?.finalAnswer || (finalState as any)?.answer || '') }, [tokens, finalState]) const onSubmit = async (event: FormEvent<HTMLFormElement>) => { event.preventDefault() if (!task.trim() || isRunning) return await run({ initialState: { task } }) } return ( <form onSubmit={onSubmit}> <input value={task} onChange={(event) => setTask(event.target.value)} disabled={isRunning} /> <button type='submit' disabled={isRunning || !task.trim()}> {isRunning ? 'Running...' : 'Run'} </button> <button type='button' onClick={reset} disabled={isRunning}>Reset</button> <div>Status: {status}</div> {error ? <div>{error}</div> : null} <pre>{answer}</pre> </form> ) }

What useNewnadoonAgent() gives you

The hook gives you direct control over the full run lifecycle.

Important fields:

  • run() starts the agent
  • status tells you whether the run is idle, running, succeeded, or failed
  • tokens gives you streamed text as it arrives
  • finalState gives you the final workflow state once the run finishes
  • error gives you runtime or connection errors
  • reset() clears the current run state

In plain language: this hook is for developers who want to fully control the frontend experience instead of using a prebuilt chat UI.

7. Use the higher-level chat hooks

If you want the logic but not the prebuilt components, Newnadoon also provides chat hooks:

  • useFullPageAgentChat()
  • usePanelAgentChat()
  • useBubbleWidgetAgentChat()

These hooks wrap useNewnadoonAgent() and add chat-specific state such as:

  • task
  • setTask
  • onSubmit
  • answer
  • event metrics and debug helpers

That means you can build a custom design while keeping the chat behavior standardized.

Which option should you choose?

Use FullPageAgentChat if:

  • the agent is the main purpose of the page
  • you want the fastest setup

Use PanelAgentChat if:

  • the agent is part of an existing dashboard or tool
  • screen space is limited

Use BubbleWidgetAgentChat if:

  • the agent should be available from anywhere in the app
  • you want a lightweight assistant entry point

Use useNewnadoonAgent() if:

  • you want complete design freedom
  • you need custom behaviors or business-specific UI logic

The example starter uses this basic structure:

app/ api/ newnadoon/ [...path]/route.ts full-page/ page.tsx panel/ page.tsx widget/ page.tsx raw-hook/ page.tsx

This gives you one shared runtime route and multiple frontend experiences using the same registered agents.

Common mistakes

If the frontend is not working, check these first:

  • the App Router API route exists at app/api/newnadoon/[...path]/route.ts
  • newnadoon.json exists and the agent id is registered
  • NEXT_PUBLIC_AGENT_ID matches an actual agent id in newnadoon.json
  • your agent file exports a default agent
  • your frontend is calling the same route base used by the hook (/api/newnadoon/run and /api/newnadoon/events)

Next step

Once the frontend can talk to your agent successfully, continue to API to understand the runtime endpoints in more detail, or review Examples for more integration patterns.