Skip to content
memghost.com Open App

Frontend Architecture

The MemGhost web interface is a modern, responsive Next.js application that provides a rich user experience while maintaining the event-driven architecture of the backend.

Technology Stack

LibraryPurpose
Next.js 14+ (App Router)React framework with server components
TypeScriptType safety throughout
Tailwind CSSUtility-first styling
shadcn/uiAccessible React components (Radix + Tailwind)
TanStack Query (React Query)Server state management
ZustandClient state management
react-hook-form + ZodForm handling with runtime validation
EventSource APIReal-time updates via SSE

Directory Structure

web/src/
├── app/ # Next.js App Router
│ ├── (auth)/ # Auth layout group (login, callback)
│ ├── (dashboard)/ # Protected routes
│ │ ├── items/ # VaultItem list, detail, edit, create
│ │ ├── people/ # People management
│ │ └── tags/ # Tag browsing
│ └── layout.tsx # Root layout
├── components/ # React components
│ ├── ui/ # shadcn/ui components
│ └── [module]/ # Module-specific components
├── lib/ # Utilities
│ ├── api/ # API client & hooks
│ ├── auth/ # Authentication (OIDC)
│ ├── events/ # Real-time SSE
│ └── store/ # Zustand stores
└── types/ # TypeScript types (generated from OpenAPI)

State Management

Server State (React Query)

React Query manages all API data — caching, automatic refetching, optimistic updates, and cache invalidation.

export function useVaultItems() {
return useQuery({
queryKey: ['items'],
queryFn: async () => {
const response = await apiClient.get('/items');
return response.data;
},
});
}

Client State (Zustand)

Zustand manages UI state: sidebar open/closed, theme preferences, filter state, modal state.

export const useUIStore = create((set) => ({
sidebarOpen: true,
setSidebarOpen: (open) => set({ sidebarOpen: open }),
}));

Real-Time Updates

The UI connects to the SSE event stream on login. When events arrive, React Query caches are invalidated and relevant queries are refetched automatically:

export function useEvents() {
const queryClient = useQueryClient();
useEffect(() => {
eventSourceManager.connect();
eventSourceManager.subscribe('item.created.v1', () => {
queryClient.invalidateQueries({ queryKey: ['items'] });
});
}, [queryClient]);
}

Authentication

Using OpenID Connect with the backend as identity provider:

  1. User navigates to a protected route
  2. Redirected to login if not authenticated
  3. OIDC authentication flow completes
  4. Token stored in localStorage and included in API requests
  5. Automatic token refresh on expiration

Routes under (dashboard)/ are protected and redirect to login when unauthenticated.

Component Patterns

  • Server Components — initial page load, SEO-friendly, fast render
  • Client Components — forms, real-time updates, user interactions
  • Form Handling — react-hook-form with Zod validation schemas

Performance

  • Server Components for initial data fetch
  • React Query caching avoids redundant API calls
  • Optimistic updates for immediate UI feedback
  • Code splitting with dynamic imports
  • Next.js Image component for optimization

Accessibility

  • WCAG AA compliance target
  • Keyboard navigation throughout
  • Screen reader support with ARIA labels
  • Focus management on navigation
  • All shadcn/ui components are accessible by default