- Overview
- Architecture
- File Structure
- Core Concepts
- Component Guide
- Utilities Guide
- State Management
- Development Guide
- Feature Implementation Details
- Future Enhancements
JSON Simplify is a frontend-only web application built with Next.js 15, React, TypeScript, and Tailwind CSS. It helps users visualize, navigate, and explore JSON data structures through an intuitive tree interface.
- β JSON input via paste or file upload
- β Interactive tree visualization
- β Path navigation with bracket notation
- β Search functionality with auto-expand
- β Copy paths and values to clipboard
- β Shareable URLs with compressed JSON
- β Dark mode UI
- Framework: Next.js 15 (App Router)
- UI Library: React 19
- Language: TypeScript
- Styling: Tailwind CSS
- Compression: pako (for URL sharing)
- Modular Components: Each component has a single, clear responsibility
- DRY (Don't Repeat Yourself): Common logic is extracted into utility functions
- Type Safety: Comprehensive TypeScript types for all data structures
- Junior-Friendly: Well-commented code with clear naming conventions
- Frontend-Only: No backend, no database, fully client-side
User Input (JSON)
β
Validation & Parsing
β
Tree Structure Generation
β
State Management (React Hooks)
β
Component Rendering
β
User Interactions (Click, Search, Copy)
json-simplify/
βββ app/
β βββ page.tsx # Main application page (orchestrates all components)
β βββ layout.tsx # Root layout with metadata
β βββ globals.css # Global styles
β
βββ components/
β βββ JsonInput.tsx # Left panel: JSON input and validation
β βββ TreeViewer.tsx # Right panel: Tree structure display
β βββ TreeNodeComponent.tsx # Individual tree node (recursive)
β βββ PathPreview.tsx # Bottom panel: Path and value preview
β βββ SearchBar.tsx # Search input with match counter
β βββ ShareButton.tsx # Share URL generator
β
βββ lib/
β βββ types.ts # TypeScript type definitions
β βββ jsonUtils.ts # JSON parsing, validation, tree building
β βββ urlUtils.ts # URL encoding/decoding for sharing
β βββ clipboardUtils.ts # Clipboard operations
β
βββ PRODUCT.md # This file!
app/: Next.js App Router pages and layoutscomponents/: Reusable UI components (one per file)lib/: Pure utility functions (no UI, easily testable)
The application converts JSON into a tree structure where each node contains:
interface TreeNode {
key: string; // Property name or array index
value: unknown; // The actual value
type: JsonValueType; // string, number, boolean, null, object, array
path: string; // Full path in bracket notation
children?: TreeNode[]; // Child nodes (for objects/arrays)
depth: number; // Nesting level
isExpanded?: boolean; // UI state
}Example: For JSON { "user": { "name": "John" } }:
- Root node:
key: "root",type: "object",path: "root" - Child:
key: "user",type: "object",path: "root.user" - Grandchild:
key: "name",type: "string",path: "root.user.name"
Paths use JavaScript bracket notation:
- Object properties:
user.name - Array indices:
items[0] - Nested:
user.orders[0].items[1].price
All state lives in app/page.tsx (the main page):
jsonInput: Raw JSON string from userrootNode: Parsed tree structureselectedNode: Currently selected nodeexpandedPaths: Set of expanded node pathssearchTerm: Current search query
This centralized approach makes it easy to understand data flow.
Purpose: Handle JSON input and validation
Features:
- Textarea for paste/typing
- File upload button
- Drag & drop support
- Prettify button (formats JSON)
- Clear button
- Error display
Props:
{
value: string; // Current JSON input
onChange: (json: string) => void; // Callback when input changes
error: string | null; // Error message to display
}Key Functions:
handleTextChange: Updates state on typinghandleFileUpload: Reads .json fileshandlePrettify: Formats JSON with 2-space indentation
Purpose: Display the JSON tree structure
Features:
- Recursive tree rendering
- Empty state message
- Sticky header
Props:
{
rootNode: TreeNode | null; // Root of tree
onNodeClick: (node: TreeNode) => void; // Click handler
selectedPath: string | null; // Currently selected path
searchTerm: string; // Search query
expandedPaths: Set<string>; // Expanded node paths
onToggleExpand: (path: string) => void; // Expand/collapse handler
}Purpose: Render individual tree nodes (and their children)
Features:
- Click to expand/collapse (objects/arrays)
- Click to copy path (leaf values)
- Color-coded by type
- Search highlighting
- Drag-to-select value copying
Props:
{
node: TreeNode; // The node to render
onToggleExpand: (path: string) => void;
onNodeClick: (node: TreeNode) => void;
isExpanded: boolean; // Is this node expanded?
isSelected: boolean; // Is this node selected?
searchTerm: string; // For highlighting
expandedPaths: Set<string>; // Pass to children
}Key Behavior:
- Leaf nodes (strings, numbers, etc.): Click copies path
- Objects/arrays: Click toggles expand/collapse
- Recursively renders children when expanded
Purpose: Show path and value of selected node
Features:
- Path display with copy button
- Value preview (formatted for objects/arrays)
- Copy value button (JSON-safe)
- Type indicator
Props:
{
selectedNode: TreeNode | null; // Node to preview
}Purpose: Search for keys in the JSON tree
Features:
- Text input with clear button
- Match counter
- Real-time search
Props:
{
searchTerm: string;
onSearchChange: (term: string) => void;
matchCount?: number;
}Purpose: Generate shareable URLs
Features:
- Compresses JSON using pako
- Encodes to URL-safe base64
- Copies to clipboard
- Handles size limits (warns if too large)
Props:
{
jsonString: string; // JSON to share
disabled?: boolean; // Disable if no valid JSON
}Checks if a string is valid JSON.
const result = validateJson('{"key": "value"}');
// { isValid: true, error: null }Formats JSON with 2-space indentation.
Core function: Converts JSON object to TreeNode structure.
- Recursively processes objects and arrays
- Builds paths in bracket notation
- Auto-expands root level
Returns array of paths that match search term (case-insensitive).
Returns all parent paths for a given path.
getParentPaths('user.orders[0].items[1]')
// ['user', 'user.orders', 'user.orders[0]', 'user.orders[0].items']Formats values for display:
- Arrays:
Array(5) - Objects:
Object(3) - Strings:
"text" - Others: raw value
Compresses and encodes JSON for URL.
- Compress with pako (deflate)
- Convert to base64
- Make URL-safe (replace +, /, =)
Reverses the encoding process.
Generates full shareable URL: https://example.com?json=encoded_data
Extracts and decodes JSON from current URL (for loading shared links).
Copies text to clipboard using modern API with fallback.
Simple notification (currently console.log, can be enhanced with toast library).
All TypeScript types are defined here:
JsonValueType: Type enumTreeNode: Core data structureSearchMatch: Search result- Component props interfaces
Why separate types file?
- Easy to find and modify
- Can be imported anywhere
- Prevents circular dependencies
All state is managed in app/page.tsx using React hooks:
jsonInput: Raw JSON string from userjsonError: Validation error messagerootNode: Parsed tree structureselectedNode: Currently selected nodesearchTerm: Search queryexpandedPaths: Set of expanded node paths
-
Load from URL (on mount):
useEffect(() => { const urlJson = getJsonFromUrl(); if (urlJson) setJsonInput(urlJson); }, []);
-
Parse JSON (when input changes):
useEffect(() => { // Validate β Parse β Build tree β Auto-expand first level }, [jsonInput]);
-
Handle Search (when search term changes):
useEffect(() => { // Find matches β Expand parent paths }, [searchTerm, rootNode]);
- Simple: All state in one place
- Predictable: Clear data flow
- Debuggable: Easy to see what state changes when
- Junior-friendly: No complex state libraries needed
- Node.js 18+ or npm
- npm, yarn, or pnpm
# Install dependencies
npm install
# Run development server
npm run dev
# Build for production
npm run build
# Start production server
npm startExample: Add "Copy Subtree" button
-
Add utility function (
lib/jsonUtils.ts):export function copySubtree(node: TreeNode): string { return JSON.stringify(node.value, null, 2); }
-
Use in component (
components/PathPreview.tsx):const handleCopySubtree = async () => { const json = copySubtree(selectedNode); await copyToClipboard(json); };
-
Add button to UI:
<button onClick={handleCopySubtree}>Copy Subtree</button>
- Check state: Add
console.log(state)in components - Validate JSON: Use
validateJson()to check input - Inspect tree: Log
rootNodeto see structure - Check paths: Verify path building with
buildPath()
When: User clicks a leaf value (string, number, etc.)
Flow:
TreeNodeComponentdetects click on leaf- Calls
copyToClipboard(node.path) - Shows "β Path copied" hint
- Updates
selectedNodein parent
Code (TreeNodeComponent.tsx):
const handleClick = () => {
if (isLeaf) {
copyToClipboard(node.path);
onNodeClick(node);
setShowCopyHint(true);
setTimeout(() => setShowCopyHint(false), 1500);
}
};When: User drags to select text in a value
Flow:
- User drags to select value text
- On
mouseUp, check if selection exists - Copy JSON-safe value using
toJsonString()
Code (TreeNodeComponent.tsx):
const handleValueMouseUp = async (e: React.MouseEvent) => {
const selection = window.getSelection();
if (selection && selection.toString().length > 0) {
e.stopPropagation();
await copyToClipboard(toJsonString(node.value));
}
};When: User types in search bar
Flow:
searchTree()finds matching pathsgetParentPaths()gets all ancestor paths- Update
expandedPathsto include matches + ancestors - Tree re-renders with nodes expanded
Code (app/page.tsx):
useEffect(() => {
if (searchTerm && rootNode) {
const matches = searchTree(rootNode, searchTerm);
const pathsToExpand = new Set<string>();
matches.forEach(matchPath => {
const parents = getParentPaths(matchPath);
parents.forEach(path => pathsToExpand.add(path));
pathsToExpand.add(matchPath);
});
setExpandedPaths(pathsToExpand);
}
}, [searchTerm, rootNode]);When: User clicks "Share" button
Flow:
- Compress JSON with pako (deflate algorithm)
- Convert to base64
- Make URL-safe (replace special chars)
- Create URL:
?json=encoded_data - Copy to clipboard
- On page load, check for
?json=parameter and decode
Size Limit: URLs over 2000 chars show error (browser limitation)
Code (lib/urlUtils.ts):
export function encodeJsonToUrl(json: string): string {
const compressed = pako.deflate(json, { level: 9 });
const base64 = btoa(String.fromCharCode(...compressed));
return base64.replace(/\+/g, '-').replace(/\//g, '_').replace(/=/g, '');
}State: expandedPaths is a Set<string> of paths that are expanded
Toggle Logic:
const handleToggleExpand = (path: string) => {
setExpandedPaths(prev => {
const next = new Set(prev);
if (next.has(path)) {
next.delete(path); // Collapse
} else {
next.add(path); // Expand
}
return next;
});
};Auto-Expand First Level:
if (tree.children) {
const firstLevelPaths = tree.children.map(child => child.path);
setExpandedPaths(new Set(firstLevelPaths));
}- Add keyboard shortcuts (β/β to navigate, Enter to expand)
- Add "Collapse All" / "Expand All" buttons
- Add JSON syntax highlighting in input
- Add toast notifications instead of console logs
- Add light/dark mode toggle
- Bookmark frequently used paths (LocalStorage)
- Export tree to CSV
- Compare two JSON objects (diff view)
- JSON schema validation
- Path history (recent paths)
- Large JSON performance optimization (virtual scrolling)
- JSON editing capabilities
- Custom path notation (dot vs bracket)
- Plugin system for custom formatters
- Backend storage (optional accounts)
- Components: PascalCase (
JsonInput,TreeViewer) - Functions: camelCase (
validateJson,handleClick) - Constants: UPPER_SNAKE_CASE (if any)
- Types: PascalCase (
TreeNode,JsonValueType)
- One component per file
- Related utilities grouped together
- All types in
types.ts
- File header: Purpose and features
- Function header: What it does, parameters, return value
- Inline: Complex logic only (code should be self-explanatory)
- No
anytypes (useunknownif truly unknown) - Always define prop interfaces
- Use type inference where possible
- Utility functions (
jsonUtils,urlUtils,clipboardUtils) - Pure functions are easy to test
- Render tests (does it show expected UI?)
- Interaction tests (clicks, inputs)
- Use React Testing Library
- Full user flows (paste JSON β navigate β copy path)
- Use Playwright or Cypress
describe('validateJson', () => {
it('returns valid for correct JSON', () => {
const result = validateJson('{"key": "value"}');
expect(result.isValid).toBe(true);
});
it('returns error for invalid JSON', () => {
const result = validateJson('{invalid}');
expect(result.isValid).toBe(false);
expect(result.error).toBeTruthy();
});
});- Hooks:
useState,useEffect,useMemo,useRef - Props: Passing data between components
- State lifting: Parent manages state, children receive props
- Conditional rendering:
{condition && <Component />} - Lists:
array.map()with keys
- Interfaces: Define object shapes
- Type aliases:
type JsonValueType = 'string' | 'number' | ... - Generics: (not used heavily here, but good to know)
- Union types:
string | null
- App Router:
app/directory structure - Client components:
'use client'directive - Metadata: SEO-friendly meta tags
- Read this document thoroughly
- Run the app and explore features
- Read
app/page.tsxto understand state flow - Pick a component and trace its code
- Make a small change (e.g., add a button)
- Test your change in the browser
- Ask questions if anything is unclear
- Code follows naming conventions
- Functions are well-commented
- No console errors or warnings
- TypeScript types are correct
- UI looks good in dark mode
- Feature works on Chrome, Firefox, Safari
Issue: JSON not parsing
- Fix: Check for syntax errors, trailing commas, unquoted keys
Issue: Tree not expanding
- Fix: Check
expandedPathsstate, verify path matching logic
Issue: Copy not working
- Fix: Check clipboard permissions (HTTPS required for modern API)
Issue: Share URL too long
- Fix: Expected behavior for large JSON (browser limitation)
- Tree rendering is recursive - may be slow for deeply nested JSON
- No virtualization - large arrays (1000+ items) may lag
- Compression reduces URL size but adds processing time
If you want to add analytics later:
- Events to track: JSON size, tree depth, copy actions, share clicks
- Tools: Google Analytics, Mixpanel, PostHog
- Privacy: No PII, JSON content never sent to servers
- No server: No injection attacks, no data leaks
- URL encoding: JSON is compressed, not human-readable in URL
- Clipboard: Uses secure APIs (requires HTTPS)
- File upload: Client-side only, no upload to server
Note: Users should not paste sensitive data (passwords, tokens) into a public web app.
- License: (Add your license here - MIT, Apache, etc.)
- Author: (Your name/team)
- Inspiration: Postman JSON viewer, JSON Editor Online
JSON Simplify is designed to be:
- β Simple: Easy to understand and modify
- β Maintainable: Well-organized, well-documented code
- β Junior-friendly: Clear patterns, no magic
- β Production-ready: Full feature set, error handling
Next Steps:
- Deploy to Vercel/Netlify
- Add tests
- Gather user feedback
- Iterate on features
Questions? Check the code comments or reach out to the team!
Last Updated: December 2025 Version: 1.0.0