Sovereign AI Ecosystem

'Complete Guide: Building an AI-Powered Next.js Application with Mastra and [post] deterministic

A comprehensive guide to building a Next.js application with Mastra and

MastraOllamaNext.jsAI AgentsTask AutomationReal-Time StreamingAgent WorkflowsWeb DevelopmentAI IntegrationProduction Deployment

![Image](/images/ComfyUI_00205_.png)

Building an AI-Powered Next.js Application with Mastra and Ollama

1. Introduction

The world of AI is rapidly evolving, with agent-based systems emerging as powerful tools for task automation and complex problem-solving. In this comprehensive tutorial, we'll walk through building a sophisticated Next.js application that integrates **Mastra** (a production-ready AI agent framework) with **Ollama** (an open-source local LLM runner) to create an intelligent task automation system.

**What is Mastra?** Mastra is an enterprise-grade framework for creating autonomous AI agents with advanced reasoning capabilities, built-in workflow management, and production-ready features. It enables developers to build reliable, observable AI agents that can decompose complex tasks into manageable steps and execute them methodically.

**What is Ollama?** Ollama allows you to run large language models (LLMs) locally on your machine rather than relying on cloud APIs. This approach provides privacy benefits, reduces costs, and eliminates API latency issues—making it ideal for development and privacy-sensitive applications.

By the end of this tutorial, you'll have created a web application where users can submit goals like "Create a content calendar for social media" or "Analyze quarterly sales data," and watch as an AI agent systematically works through the problem, documenting its reasoning and producing high-quality results.

2. Setting Up the Project

2.1 Prerequisites

Before starting, ensure you have: - Node.js 18+ installed - Basic knowledge of React and Next.js - Ollama installed (we'll cover this in detail) - A Mastra account (we'll help you set this up)

2.2 Creating a Next.js Application

Let's begin by creating a fresh Next.js project:

bash npx create-next-app@latest mastra-ollama-app cd mastra-ollama-app

During the setup, select the following options: - Would you like to use TypeScript? → Yes (for type safety) - Would you like to use ESLint? → Yes - Would you like to use Tailwind CSS? → Yes (for styling) - Would you like to use the src/ directory? → Yes (for organization) - Would you like to use App Router? → Yes (for modern routing) - Would you like to customize the default import alias? → No

2.3 Installing Dependencies

Install the Mastra client library and other necessary packages:

bash npm install @mastraai/client ollama-js dotenv react-markdown

2.4 Setting Up Ollama

1. Visit [Ollama's official website](https://ollama.com/) and download the installer for your operating system. 2. Install Ollama following the on-screen instructions. 3. Open a terminal and pull the Mistral model (a powerful open-source LLM):

bash ollama pull mistral

This will download the model, which may take several minutes depending on your internet connection.

2.5 Setting Up Mastra

1. Visit [Mastra's website](https://mastra.ai) and create an account 2. Generate an API key from your dashboard 3. Create a .env.local file in your project root with:

MASTRA_API_KEY=your_api_key_here

2.6 Verifying Your Setup

Let's ensure Ollama is working correctly:

bash ollama run mistral "What can you help me with today?"

You should see a coherent response from the model, confirming Ollama is properly installed.

3. Understanding the Frontend (React + Next.js)

Now, let's build a responsive, user-friendly interface for our agent application.

3.1 Creating the Home Page Component

Create or replace the file at src/app/page.tsx with:

```tsx "use client"; import { useState, useRef, useEffect } from "react"; import ReactMarkdown from "react-markdown";

export default function Home() { const [goal, setGoal] = useState<string>(""); const [logs, setLogs] = useState<string[]>([]); const [isRunning, setIsRunning] = useState<boolean>(false); const [result, setResult] = useState<string>(""); const logsEndRef = useRef<HTMLDivElement>(null);

// Auto-scroll to the bottom of logs useEffect(() => { if (logsEndRef.current) { logsEndRef.current.scrollIntoView({ behavior: "smooth" }); } }, [logs]);

const handleRunAgent = async () => { if (!goal.trim() || isRunning) return; setIsRunning(true); setLogs(["🤖 Initializing Mastra agent powered by Ollama..."]); setResult(""); try { const response = await fetch("/api/run-agent", { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ goal }), }); if (!response.ok) { const errorData = await response.json(); throw new Error(errorData.error || "Failed to run agent"); } // Use streaming for real-time updates const reader = response.body?.getReader(); const decoder = new TextDecoder(); if (reader) { while (true) { const { done, value } = await reader.read(); if (done) break; const text = decoder.decode(value); const data = JSON.parse(text); if (data.type === "log") { setLogs(logs => [...logs, data.message]); } else if (data.type === "result") { setResult(data.content); } } } } catch (error: any) { setLogs(logs => [...logs, ❌ Error: ${error.message}]); } finally { setIsRunning(false); setLogs(logs => [...logs, "✅ Agent execution completed"]); } };

return ( <main className="flex min-h-screen flex-col items-center p-8 max-w-5xl mx-auto"> <h1 className="text-4xl font-bold mb-3">AI Agent Workspace</h1> <h2 className="text-xl text-gray-600 mb-8">Powered by Mastra + Ollama</h2> <div className="w-full space-y-8"> {/* Goal Input Section */} <div className="bg-white p-6 rounded-lg shadow-md"> <h3 className="text-lg font-semibold mb-3">What would you like the agent to accomplish?</h3> <div className="flex gap-3"> <input type="text" placeholder="e.g., Create a marketing plan for a new product launch" value={goal} onChange={(e) => setGoal(e.target.value)} className="flex-1 p-3 border rounded-md text-gray-800 focus:ring-2 focus:ring-blue-500" disabled={isRunning} /> <button onClick={handleRunAgent} disabled={isRunning || !goal.trim()} className={px-6 py-3 rounded-md font-medium transition ${ isRunning ? "bg-gray-300 text-gray-600" : "bg-blue-600 text-white hover:bg-blue-700" }} > {isRunning ? "Working..." : "Run Agent"} </button> </div> </div> {/* Agent Logs Section */} <div className="bg-gray-50 rounded-lg shadow-md"> <div className="bg-gray-100 p-4 rounded-t-lg border-b"> <h3 className="text-lg font-semibold">Agent Thinking Process</h3> </div> <div className="p-4 max-h-80 overflow-y-auto"> {logs.length === 0 ? ( <p className="text-gray-500 italic">Agent logs will appear here...</p> ) : ( <div className="space-y-2"> {logs.map((log, index) => ( <div key={index} className="p-3 bg-white rounded border"> {log} </div> ))} <div ref={logsEndRef} /> </div> )} </div> </div> {/* Result Section */} {result && ( <div className="bg-white rounded-lg shadow-md"> <div className="bg-green-100 p-4 rounded-t-lg border-b"> <h3 className="text-lg font-semibold text-green-800">Agent Result</h3> </div> <div className="p-6 prose

Sources

DanielKliewer.com blog · source

Related (0)

No recorded relationships.

← all Blog