Building a responsive AI chat prototype with attachments and local persistence
AI chat interfaces are no longer simple text boxes with a send button. The latest prototypes push far beyond the basics: persistent multi-conversation workspaces, safe file attachments, public sharing, and a polished, mobile-friendly UI. Building a responsive AI chat prototype with local persistence means you deliver an app that feels reliable, interactive, and ready for real work — not just demo chats. Here's how a modern stack delivers this, and what you need to focus on if you want to build or extend a similar workspace today.
What makes a responsive AI chat prototype complex
Building a feature-complete AI chat workspace is fundamentally different from building a basic chat UI.
Core challenge: you have to juggle more than one chat at once, keep histories persistent, link files and messages, and support everything across devices without backend chaos.
- Multi-conversation management: Users expect to work on several threads at once. Each conversation needs a unique chat ID and title, reliable history, and quick switching. Temporary chats and stable IDs become baseline features.
- Project workspaces: Modern users treat chats as ongoing projects — planning, writing, debugging, reviewing files. The app needs solid project-level context, not just a flat thread.
- Persistent history: Relying only on in-memory state loses chats on refresh. Persisting chat state in browser localStorage enables reloads and offline use — MDN documents localStorage as per-origin storage that persists across browser sessions — but it introduces new sync, privacy, and quota tradeoffs.
- File attachments: Users want to review files, send code snippets, or get AI explanations for uploads. Handling attachments means safely caching them, preventing malicious uploads, and protecting UI performance.
- Response regeneration and sharing: Each message may have multiple AI-generated versions. Switching between, copying, or sharing them demands a dynamic, solid API.
- UI complexity: Cleanly managing the above — without turning the codebase into a mess — means splitting responsibilities, isolating state, and designing sane workflows. Keeping the repo agent-readable pays off fast once AI assistants start extending it.
- Mobile-first design: With users expecting chat anywhere, layouts and interactions must adapt between desktop and mobile, including touch and small-screen optimizations.
Takeaway: the Claude-style chat workspace model enables capable workflows but multiplies architecture and UX complexity. Without careful design, features collide and state leaks.
Which technologies power this AI chat prototype
A modern AI chat prototype needs more than just JavaScript — it needs a coordinated stack that supports performance, safety, and fast iteration. Here's what delivers.
- Next.js with App Router: Structured routing for conversations and project workspaces, API routes for backend logic like AI proxying, and a React-first full-stack model. Next.js's own docs organize around the App Router as the current path for new applications — start there, not in legacy routing patterns.
- React + TypeScript: React powers component-based UI with hooks and built-in concurrency, while TypeScript prevents type errors as features and state multiply.
- Tailwind CSS: Utility-first styling — glassmorphism effects, responsive breakpoints, and dark/light themes stay quick and maintainable. Class extraction and props-based variants allow deep theming with minimal code.
- Server-proxied model API: All chat requests flow through a server-side API route that relays them to the model provider, keeping the API key server-only and out of the client. This shields backend credentials and adds a catch-all validation layer. (The demo uses Google's Gemini API; check the provider's current API docs for the latest endpoint shape before copying request formats.)
- Testing with Vitest and React Testing Library: Repeatable UI and unit tests plus headless browser tests keep features like state sync, attachments, and titles stable during change.
- Persistence via browser localStorage: All chat, conversation, and project data is saved in localStorage. It's fast, supports offline and refresh persistence, and needs no external accounts or cloud setup. Watch the quota: browsers commonly cap storage at around 5MB per origin, so this fits hundreds of chats plus metadata — not binary files or endless history.
Sample project structure (illustrative route-handler pseudocode):
// app/api/chat/route.ts (illustrative — check current docs for exact shape)
export async function POST(req: Request) {
const { contents } = await req.json();
// Proxy chat to the model API here; keep your API key server-side
const apiRes = await fetch(MODEL_API_URL, {
method: 'POST',
headers: { Authorization: `Bearer ${process.env.MODEL_API_KEY}` },
body: JSON.stringify({ contents }),
});
return Response.json(await apiRes.json());
}Takeaway: modern AI chat prototypes are only possible with a coordinated stack — a React full-stack framework, TypeScript, utility-first CSS, and server-proxied AI APIs.
11 production screens. Login, database, payments — all wired.
The SaaS Dashboard Kit ships everything already connected. Nothing to set up. Live demo at saas.otf-kit.dev.
How local persistence works in AI chat apps
Store all chats, projects, and attachment indexes inside browser localStorage using structured keys and compact serialization. You get offline capability and privacy by default, but you must respect storage limits.
- localStorage mechanics: Storage is scoped per origin and persists across sessions (unlike sessionStorage, which clears when the page session ends). That fits hundreds of chats, conversation metadata, and lightweight attachment indexes — but not big binary files or unbounded history.
- Data model: Structure keys by user, session, or project — e.g.
chat:0001,project:main— and store JSON blobs for each, or a single master state if you need atomicity. Attachments are stored as URLs or references, never their full data, to fit within quotas.
// Chat record (simplified)
type ChatRecord = {
id: string
projectId: string
history: { role: 'user' | 'assistant'; content: string }[]
attachments: AttachmentMeta[]
lastActive: number
}
// Save to localStorage
localStorage.setItem(`chat:${id}`, JSON.stringify(chatRecord))
// Read from localStorage
const chat = JSON.parse(localStorage.getItem(`chat:${id}`) ?? '{}')- Sync and offline handling: When a user moves between devices or clears storage, history is lost unless you implement export/import or cloud sync. For privacy and simplicity, this demo is offline-only.
- Tradeoffs: You avoid backend complexity and data-residency linkages, but accept possible data loss (localStorage isn't forever), quota limits, and less "magic" sync. For many users, this is the right balance for private session history.
Takeaway: localStorage is a simple, solid way to persist AI chat data for individuals — but don't use it for enterprise-grade sharing, and watch size limits.
How to manage file attachments in AI chat interfaces
Only lightweight file metadata and URLs should be stored locally; actual files are linked by session, cached in-memory, or temporarily stored with controlled APIs. Attachments are never sent unless explicitly included by the user.
- Supported files: The demo focuses on safe types — PDFs, images, plain text, markdown, and code snippets. Avoid executable or scriptable files for safety.
- Caching and linking: When a user attaches a file, a temporary URL (via
URL.createObjectURL) or metadata reference is stored and linked to the active conversation. Attachment previews render for images; names and types for everything else. - UI and UX: The chat message UI shows attachment icons or inline previews. On mobile, touch and hold is supported for attachments. Strict file size limits (e.g. under 2MB per file) plus overall quotas keep localStorage from blowing through its cap.
- Security: Always sanitize filenames, strip offending metadata, and — crucially — never execute or preview unsafe types. Attachment previews are sanitized at the UI tier.
// Accept and preview an upload (illustrative)
function handleFileUpload(file: File) {
if (file.size > MAX_ATTACHMENT_SIZE) return alert('File too large');
const objectUrl = URL.createObjectURL(file);
setAttachments(prev => [...prev, { name: file.name, url: objectUrl, type: file.type }]);
}Takeaway: attachments add real workflow value, but only when handled safely — with clear limits, never storing full files in localStorage, and never trusting user input.
How to build a polished and responsive UI for AI chat
Use utility-first CSS for glassmorphism effects and responsive breakpoints; design layouts for flexible resizing and mobile, with clean project and chat navigation.
- Glassmorphism styling: Semi-transparent cards, blur effects, and gradient backgrounds come from utilities like
backdrop-blur, opacity, and color stops. A real example:
<div className="bg-white/30 backdrop-blur-md rounded-xl p-4 shadow-lg">
{/* chat history and input */}
</div>- Responsive layouts: Responsive prefixes (
sm:,md:,lg:,xl:) allow conditional flex and glass layouts. The workspace sidebar, chat list, and main thread stack cleanly on mobile. - Touch interaction and mobile UI: Main actions are easily tapped. Tab interfaces and swipes allow conversation switching, while chat input remains fixed and accessible. The same one-component API thinking that unifies web and native design systems applies here: one interaction model, adapted per surface.
- Project workspaces and tabbed navigation: Each workspace includes multiple chats, quickly accessed by tabs or a sidebar. Active chat and attachments are visible at a glance.
Before your prototype meets real users, run it through a ship checklist — auth, rate limits, and cost guards are where demos usually break.
Want to start from a codebase that's already structured for this? Browse the OTF kits — production-ready starters your AI coding agent can extend.
Sources
- MDN, "Window: localStorage property" — https://developer.mozilla.org/en-US/docs/Web/API/Window/localStorage
- Next.js, official documentation — https://nextjs.org/docs
Ship the product, not the setup.
- 11 production screens — auth, billing, team, analytics, settings
- Real database, payments, and login — all wired on day 1
- AI configs pre-tuned so your agent extends instead of regenerates