Modern React app for interactive mapping with a friendly chat side panel. Built with Vite, Leaflet, and a compact UI layer.
- Interactive map (Leaflet) with custom styled markers and smooth resize handling
- Floating chat input over the map with keyboard shortcuts (Enter/Shift+Enter)
- Chat sidebar with message list, basic stats, and delete actions
- Clean header with gradient branding and responsive navigation
- ErrorBoundary to catch runtime errors with a friendly fallback
- LoadingSpinner with accessible labels
- Simple design utilities (Button, Input, Icon) and small helper library
- React 18, Vite 5
- Leaflet + react-leaflet
- Tailwind CSS (via CDN for utilities) plus minimal custom CSS in
styles/globals.css
Requirements
- Node 18+ and npm 9+
Install and run (Windows PowerShell)
# Install deps
npm install
# Start dev server (Vite prints the local URL)
npm run dev
# Build for production (outputs to dist/)
npm run build
# Preview the production build
npm run previewIf the dev port is already in use
Get-Process node -ErrorAction SilentlyContinue | Stop-Process -Force
npm run devsrc/
app.jsx # App shell (Header + MainContent) wrapped with ErrorBoundary
main.jsx # React root (StrictMode)
components/
layout/
Header.jsx # Gradient header with responsive nav
MainContent.jsx # Wires Map + Sidebar + FloatingChatInput
ui/
MapContainer.jsx # Leaflet map with custom markers + ResizeObserver helper
Sidebar.jsx # Chat list + stats (includes a small inline ChatMessage component)
FloatingChatInput.jsx # Overlay chat input (z-40) with validation and loading state
LoadingSpinner.jsx # Reusable spinner
ErrorBoundary.jsx # Error fallback UI
common.jsx # Button, Input, Icon primitives
hooks/
useMessages.js # Message state (add/remove/update) + memoized stats
styles/
globals.css # Minimal global styles/animations/utilities
utils/
constants.js # Centralized constants (layout, placeholders, mock data)
helpers.js # Small helpers (ids, formatting, classNames, validation)
index.html # HTML template, Tailwind CDN, and app mount
vite.config.js # Vite config (React plugin)
tailwind.config.js # Tailwind config (kept for future, CDN used at runtime)
postcss.config.js # PostCSS config (optional future pipeline)
High level flow
Apprenders the layout:Headeron top andMainContentin a full-height region.MainContentcomposes:MapContainer(left),Sidebar(right), andFloatingChatInputoverlayed on the map.useMessagesmanages message state;FloatingChatInputcallsonSendMessage;Sidebardisplays messages and delete actions.- A simple mock “bot” reply in
MainContentsimulates responses.
Layout and sizing
- The main region uses
height: calc(100vh - 4rem)(header is 4rem) to ensure the map fills the screen. MapContaineris absolutely positioned to fill its parent (inset-0, 100% width/height).- The floating chat input is positioned with
absolute bottom-4 left-4 right-4and elevated viaz-40.
Key files
src/utils/constants.js— sizes, z-index, placeholders, mock marker data.src/utils/helpers.js—validateTextInput,classNames, id generation, etc.tailwind.config.js— available if you later switch from CDN utilities to a compiled Tailwind pipeline.
Tailwind usage
- The project currently uses Tailwind via CDN (see
index.html). styles/globals.cssadds minimal utilities/animations and is automatically applied via the HTML<style>rules and class utilities; if you prefer a compiled Tailwind setup, wireglobals.cssintomain.jsxand run a PostCSS/Tailwind build.
Header— gradient background, responsive nav, accessible controls.MapContainer—react-leafletmap, custom divIcon markers,ResizeObserverto keep the map sized.FloatingChatInput— validation (validateTextInput), loading state, Enter to send / Shift+Enter new line.Sidebar— message list, stats, empty state; includes an inlineChatMessagefor simplicity.ErrorBoundary— friendly fallback UI with retry/refresh.LoadingSpinner— accessible spinner with optional message.
Map is a tiny strip or blank tiles
- Ensure the map’s container has a real pixel height. This repo sets the main area height with
calc(100vh - 4rem). - Check console for Leaflet CSS:
import 'leaflet/dist/leaflet.css'is inMapContainer.jsx.
Floating chat box isn’t visible / can’t click
- It must be above the map: wrapper uses
z-40andpointer-events-none; the form usespointer-events-auto. - Hard refresh the browser (Ctrl+F5) to clear stale styles.
Dev server says port is in use
Get-Process node -ErrorAction SilentlyContinue | Stop-Process -Force
npm run devBuild works but preview looks different from dev
- The CDN Tailwind is consistent across dev/preview, but if you move to compiled Tailwind, ensure the CSS is included in
index.htmlor imported inmain.jsx.
"scripts": {
"dev": "vite",
"build": "vite build",
"preview": "vite preview",
"lint": "eslint . --ext js,jsx --report-unused-disable-directives --max-warnings 0",
"format": "prettier --write \"src/**/*.{js,jsx,css,md}\""
}- Replace mock bot responses with a real backend
- Persist messages to localStorage or server
- Switch from Tailwind CDN to compiled Tailwind for full control and purging
- Theme toggle (light/dark)
- Map layer switcher and search integration
Private project (no license specified).
- Map height/layout glitches
- Leaflet must render into a container with a fixed pixel height. This project uses
h-[calc(100vh-4rem)]on the main container andabsolute inset-0for the map. - If you change header height, keep
calc(100vh - HEADER_HEIGHT)inapp.jsxin sync.
- Floating Chat overlay click-through
- Wrapper uses
pointer-events-noneand form usespointer-events-autoso only the input captures clicks. If you add buttons outside the form, they won’t receive clicks unless you also make thempointer-events-auto.
- ChatMessage export/import
- We inlined a minimal
ChatMessagewithinSidebar.jsxto avoid past bundler confusion. If you re-extract it, prefer a simple default export and import it asimport ChatMessage from './ChatMessage'.
- Tailwind via CDN vs compiled Tailwind
- CDN is great for speed and fewer build steps, but compiled Tailwind gives Purge/Design System scale. If you switch, import
styles/globals.cssinmain.jsxand configuretailwind.config.jscontent paths.
- React-Leaflet SSR/build caveats
- This app is client-only. Don’t render
react-leafletcomponents on the server. Keep Leaflet imports inside client modules.
- ErrorBoundary scope
- Only wrap the app once at a high level. Nesting multiple error boundaries around Leaflet/map nodes may hide real errors.
- Performance with large message lists
- Virtualization is not implemented. If messages grow large, add windowing (e.g., react-virtualized) to keep scrolling smooth.
State and hooks
- Keep
useMessagesas the single source of truth; expose only minimal API:messages,addMessage,removeMessage,messageStats. - Wrap handlers with
useCallbackonly when passing to children or when dependencies are stable; avoid unnecessary memoization. - Derive state, don’t duplicate it (e.g., compute message counts via
useMemo).
Components
- Prefer small presentational components with clear props. Co-locate minimal components (like
ChatMessage) next to their only consumer (Sidebar) until they’re reused. - Centralize class combinations using the
classNameshelper; avoid string concatenation logic spread across components. - Keep primitives (Button, Input, Icon) prop-light. Add variants cautiously and document defaults.
Styling
- Use utilities for 90% of cases. When a style repeats 3+ times, abstract it into a small component or class.
- Z-index discipline: define in
constants.js(e.g., HEADER=50, MAP=10, FLOATING_INPUT=40) and reference them (or Tailwind’s z classes) consistently.
Leaflet specifics
- Always import
'leaflet/dist/leaflet.css'where the map mounts (MapContainer.jsx). - Call
invalidateSize()when the container may have changed size; the includedResizeObserverhandles most cases. - Use
divIconor SVG markers for consistent theming; keep icon size/anchor predictable.
UX and accessibility
- Provide
aria-labels for icons and buttons; keep button text or accessible labels. - Show non-blocking validation errors (like the input error area in
FloatingChatInput).
Scalability patterns
- Introduce a feature folder if chat grows:
features/chat/{components,hooks,services}. - Add a persistence layer (localStorage now, server later) behind an interface (e.g.,
messageRepository.save/load). - For async calls, wrap them in a small service module; keep components thin.
Testing ideas
- Unit test
helpers.js(validation, classNames, createMessage) anduseMessages(add/remove/update). - Snapshot test small presentational components (ChatMessage, Button).
Deployment
npm run buildproducesdist/. Serve with any static host (GitHub Pages, Netlify, Vercel). Don’t forget to include the base path if your host uses subpaths.