Managing copy and text content in applications often starts simple but quickly becomes chaotic as your product grows. You begin with hardcoded strings scattered throughout your codebase, then realize you need translations, then struggle with inconsistent messaging across different parts of your app.
This guide presents an structured approach to organizing your copywriting documentation using JSON files that grows with your product. Whether you're building a simple MVP or managing a complex multi-language application, this system helps you maintain consistent, translatable, and maintainable copy across your entire product.
This is an extended explanation from this blog about How content designers can (and should) use JSON files.
The key principle is progressive complexity: start with the basics when you're small, then gradually expand the structure as your needs grow. This approach prevents over-engineering early on while ensuring you have a solid foundation for scaling.
Basic Structure (Level 1)
copydocs/
βββ en/ # English copy
βββ id/ # Indonesian copy
βββ README.md # Documentation
Core Pages Structure (Level 2)
copydocs/
βββ en/
β βββ auth.json # Authentication (login, signup, forgot password)
β βββ homepage.json # Landing/homepage content
β βββ onboarding.json # User onboarding flow
β βββ dashboard.json # Main dashboard/home after login
β βββ profile.json # User profile and account settings
β βββ errors.json # Error messages and alerts
β βββ common.json # Shared/common copy (buttons, labels, etc.)
βββ id/
β βββ auth.json
β βββ homepage.json
β βββ onboarding.json
β βββ dashboard.json
β βββ profile.json
β βββ errors.json
β βββ common.json
βββ README.md
Expanded Structure (Level 3)
copydocs/
βββ en/
β βββ auth.json # Login, signup, password reset
β βββ homepage.json # Hero, features, pricing, testimonials
β βββ onboarding.json # Welcome, setup, tutorials
β βββ dashboard.json # Main dashboard after login
β βββ profile.json # Account settings, preferences
β βββ billing.json # Payments, subscriptions, invoices
β βββ notifications.json # In-app notifications, alerts
β βββ search.json # Search functionality copy
β βββ help.json # Help center, FAQ, support
β βββ errors.json # All error messages
β βββ success.json # Success messages and confirmations
β βββ common.json # Buttons, labels, navigation
β βββ components/ # UI component-specific copy
β βββ modals.json
β βββ forms.json
β βββ tables.json
β βββ navigation.json
βββ id/
β βββ [same structure as en/]
βββ README.md
Complete Structure (Level 4) - For Generic Product App
copydocs/
βββ en/
β βββ auth.json # Authentication flows
β βββ homepage.json # Public homepage/landing
β βββ about.json # About us, company info
β βββ pricing.json # Pricing plans and features
β βββ onboarding.json # User onboarding experience
β βββ dashboard.json # Main dashboard/home
β βββ profile.json # User profile management
β βββ settings.json # App settings and preferences
β βββ billing.json # Payment and subscription
β βββ team.json # Team management (if applicable)
β βββ projects.json # Projects/workspace management
β βββ analytics.json # Analytics and reporting
β βββ integrations.json # Third-party integrations
β βββ notifications.json # Notification center
β βββ search.json # Search functionality
β βββ help.json # Help center and support
β βββ legal.json # Terms, privacy, policies
β βββ blog.json # Blog/content section
β βββ contact.json # Contact and support forms
β βββ admin.json # Admin panel (if applicable)
β βββ mobile.json # Mobile-specific copy
β βββ emails.json # Email templates and content
β βββ errors.json # Error messages
β βββ success.json # Success messages
β βββ loading.json # Loading states and placeholders
β βββ empty-states.json # Empty state messages
β βββ common.json # Shared copy across app
β βββ components/ # Component-specific copy
β βββ navigation.json # Menus, breadcrumbs
β βββ modals.json # Modal dialogs
β βββ forms.json # Form labels and validation
β βββ tables.json # Table headers and actions
β βββ buttons.json # Button text variations
β βββ tooltips.json # Tooltip messages
β βββ badges.json # Status badges and labels
β βββ cards.json # Card components
βββ id/
β βββ [exact same structure as en/]
βββ locales.json # Locale configuration
βββ translation-keys.md # Translation key conventions
βββ README.md # Complete documentation
Sample JSON Structure
auth.json
{
"login": {
"title": "Welcome Back",
"subtitle": "Sign in to your account",
"email_label": "Email Address",
"password_label": "Password",
"submit_button": "Sign In",
"forgot_password": "Forgot your password?",
"signup_link": "Don't have an account? Sign up"
},
"signup": {
"title": "Create Account",
"subtitle": "Get started with your free account",
"name_label": "Full Name",
"email_label": "Email Address",
"password_label": "Password",
"confirm_password_label": "Confirm Password",
"submit_button": "Create Account",
"login_link": "Already have an account? Sign in"
}
}
common.json
{
"buttons": {
"save": "Save",
"cancel": "Cancel",
"delete": "Delete",
"edit": "Edit",
"submit": "Submit",
"back": "Back",
"next": "Next",
"previous": "Previous"
},
"navigation": {
"home": "Home",
"dashboard": "Dashboard",
"profile": "Profile",
"settings": "Settings",
"help": "Help",
"logout": "Sign Out"
},
"status": {
"loading": "Loading...",
"saving": "Saving...",
"saved": "Saved successfully"
}
}
And for the implementation, I'll use these Javascript/React code:
JavaScript/React
// utils/copy.js
class CopyManager {
constructor(locale = 'en') {
this.locale = locale;
this.cache = {};
}
async loadCopy(page) {
if (!this.cache[page]) {
const response = await fetch(`/copydocs/${this.locale}/${page}.json`);
this.cache[page] = await response.json();
}
return this.cache[page];
}
async get(page, key) {
const copy = await this.loadCopy(page);
return this.getNestedValue(copy, key);
}
getNestedValue(obj, key) {
return key.split('.').reduce((o, k) => o?.[k], obj);
}
}
// react
// Usage in React component
import { useState, useEffect } from 'react';
function LoginForm() {
const [copy, setCopy] = useState({});
const copyManager = new CopyManager('en');
useEffect(() => {
copyManager.loadCopy('auth').then(setCopy);
}, []);
return (
<form>
<h1>{copy.login?.title}</h1>
<p>{copy.login?.subtitle}</p>
<input placeholder={copy.login?.email_label} />
<input type="password" placeholder={copy.login?.password_label} />
<button>{copy.login?.submit_button}</button>
</form>
);
}
Naming Convention for JSON Keys
The example keys above still use generic names like "title", "password_label", "submit", etc. Therefore, I recommend using a naming pattern for better readability and collaboration with developers to make it more maintainable.
I recommend the naming pattern:
{feature}{Component}{Scenario}
- Feature: The main functionality or page area (login, dashboard, billing)
- Component: The UI element type (button, modal, form, message)
- Scenario: The specific state or action (success, error, loading, empty)
Examples
Good Examples:
{
"loginFormError": "Invalid email or password",
"dashboardCardEmpty": "No data available",
"billingModalSuccess": "Payment method updated successfully",
"profileButtonSave": "Save Changes",
"searchInputPlaceholder": "Search products...",
"onboardingStepTitle": "Welcome to our platform",
"teamInviteSuccess": "Invitation sent successfully",
"projectDeleteConfirm": "Are you sure you want to delete this project?"
}
Avoid These:
{
"error1": "Something went wrong", // Not descriptive
"btn_save": "Save", // Inconsistent format
"loginError": "Invalid credentials", // Missing component
"message": "Success", // Too generic
"delete_project_confirmation": "Are you sure?" // Inconsistent case
}
Detailed Naming Guidelines
1. Feature Names (First Part)
- Use the main page or functionality area
- Keep it concise but descriptive
Examples: auth, dashboard, billing, profile, onboarding, search
2. Component Names (Second Part)
- Describe the UI element type
- Use singular form
Examples: Button, Modal, Form, Card, Input, Message, Title, Label
3. Scenario Names (Third Part)
- Describe the state, action, or context
- Be specific about the situation
Examples: Success, Error, Loading, Empty, Confirm, Cancel, Submit, Placeholder
Best Practices
- Be Consistent: Stick to your chosen pattern across all files
- Use CamelCase: Easier to read and consistent with JavaScript conventions
- Be Descriptive: Someone should understand the context from the key name
- Avoid Abbreviations: Use
Button
instead ofBtn
,Message
instead ofMsg
- Group Related Items: Keep similar functionality together in the JSON structure
- Document Edge Cases: Add comments in your README for special naming rules
Key Benefits of This Structure
- Scalable: Easy to add new languages and pages
- Organized: Logical grouping by functionality
- Maintainable: Clear separation of concerns
- Developer-friendly: Easy to import and use in code
- Translator-friendly: Clear context for each copy element
The best system is the one your team will actually use consistently. Start small with just the core pages you need today, establish clear naming conventions early, and expand the structure as your product and team grow.
And hey, if you need a UX Writer/Technical Writer with more than 8+ years experience to make your content documentation more neat?
I'm just a one form away for project collaboration.