This document describes the local-first editing approach implemented in the Next.js admin panel.
The system allows users to edit posts locally in the browser's storage first, then synchronize changes with GitHub when ready.
- Immediate Editing: All edits happen locally first, providing instant feedback
- Offline Capability: Users can edit posts without internet connection
- Conflict Resolution: Local changes take precedence, with manual sync control
- Performance: No API delays during editing sessions
User Edits → Local Storage → Manual Sync → GitHub API → Repository
↑ ↓
└─────────── Pull from GitHub ←─────────────────────────┘
Manages posts in the browser's localStorage:
interface LocalPost {
filename: string
content: string
frontmatter: Record<string, any>
lastModified: string
synced: boolean
}
// Key functions:
- getPosts(): LocalPost[] // Retrieve all local posts
- savePosts(posts: LocalPost[]) // Save all posts to localStorage
- savePost(post: LocalPost) // Save/update single post
- deletePost(filename: string) // Remove post from local storageFeatures:
- Automatic JSON serialization/deserialization
- Timestamp tracking for last modifications
- Sync status tracking
- Error handling for localStorage operations
Handles synchronization between local storage and GitHub:
// Key functions:
- pullFromGitHub(): Promise<LocalPost[]> // Fetch from GitHub, merge with local
- syncAllPosts(): Promise<void> // Push all local changes to GitHubPull Strategy:
- Fetch all posts from GitHub repository
- Parse frontmatter and content
- Merge with existing local posts (local takes precedence)
- Update sync status
Push Strategy:
- Get all local posts marked as unsynced
- Create/update files in GitHub repository
- Update local sync status
- Handle errors gracefully
The main page orchestrates the local-first workflow:
State Management:
const [posts, setPosts] = useState<LocalPost[]>([])
const [selectedPost, setSelectedPost] = useState<LocalPost | null>(null)
const [syncStatus, setSyncStatus] = useState<string>('')
const [isSyncing, setIsSyncing] = useState(false)Key Workflows:
- Load posts from localStorage on component mount
- If user is authenticated, pull latest from GitHub
- Merge remote changes with local posts
- User selects a post → loads into Toast UI Editor
- User makes changes → automatically saved to localStorage
- Post marked as
synced: false - User can continue editing or sync when ready
- Pull from GitHub: Fetches latest changes, merges with local
- Sync to GitHub: Pushes all local changes to repository
- Updates sync status and timestamps
- Pull from GitHub: Blue button to fetch latest changes
- Sync to GitHub: Purple button to push local changes
- Status Indicators: Real-time feedback on sync operations
- Local Editing: Immediate save to localStorage
- Visual Indicators: Shows which posts are synced/unsynced
- Conflict Resolution: Local changes take precedence
- Error Display: Shows API errors and sync issues
- Sync Status: Real-time feedback on operations
- User Context: Shows current user and authentication status
- Instant Editing: No API delays during typing
- Reduced API Calls: Only sync when explicitly requested
- Offline Capability: Edit without internet connection
- No Data Loss: Changes saved immediately to localStorage
- Flexible Workflow: Edit multiple posts before syncing
- Clear Status: Always know what's synced and what isn't
- Network Independence: Works without stable internet
- Error Recovery: Failed syncs don't lose local changes
- Conflict Prevention: Local-first approach avoids merge conflicts
// localStorage key structure
const STORAGE_KEY = 'astro-admin-posts'
// Data format
{
"posts": [
{
"filename": "example-post.md",
"content": "# My Post\n\nContent here...",
"frontmatter": {
"title": "My Post",
"date": "2024-01-01",
"tags": ["example"]
},
"lastModified": "2024-01-01T12:00:00Z",
"synced": false
}
]
}- Uses authenticated GitHub API calls
- Handles file creation, updates, and deletions
- Manages base64 encoding for file content
- Implements proper error handling and retries
- Extracts YAML frontmatter from markdown files
- Preserves formatting and structure
- Handles edge cases and malformed frontmatter
# GitHub OAuth
GITHUB_CLIENT_ID=your_client_id
GITHUB_CLIENT_SECRET=your_client_secret
# NextAuth
NEXTAUTH_SECRET=your_secret_key
NEXTAUTH_URL=http://localhost:3000
# Repository
GITHUB_REPO_OWNER=your_username
GITHUB_REPO_NAME=your_repo_name{
"dependencies": {
"next": "^15.0.0",
"next-auth": "^4.24.0",
"@octokit/rest": "^20.0.0",
"@toast-ui/react-editor": "^3.2.0",
"js-yaml": "^4.1.0"
}
}- Configure GitHub OAuth application
- Set up environment variables
- Install dependencies
- Start development server
- Sign in with GitHub account
- Pull latest changes from repository
- Edit posts locally (auto-saved)
- Sync changes when ready
- Sign out when done
- Pull from GitHub at start of editing session
- Sync changes regularly to avoid conflicts
- Review changes before syncing
- Keep local storage clean (sync regularly)
- Failed API calls don't affect local editing
- Retry mechanisms for transient failures
- Clear error messages to user
- Graceful handling of expired tokens
- Redirect to sign-in when needed
- Preserve local changes during re-authentication
- Validation of localStorage data
- Fallback to empty state if corrupted
- Recovery mechanisms for malformed posts
- Selective Sync: Choose which posts to sync
- Conflict Resolution: UI for handling merge conflicts
- Sync History: Track sync operations and changes
- Service Worker: Cache posts for offline editing
- Background Sync: Automatic sync when online
- Offline Indicators: Show connection status
- Real-time Editing: Multiple users editing simultaneously
- Change Tracking: See who made what changes
- Comments: Add notes to posts during editing
-
Posts not loading
- Check localStorage in browser dev tools
- Verify GitHub authentication
- Check network connectivity
-
Sync failures
- Verify GitHub repository permissions
- Check API rate limits
- Review error messages in status bar
-
Data loss
- Check localStorage for backup data
- Verify sync status before making changes
- Use browser dev tools to inspect data
- Browser localStorage inspection
- Network tab for API calls
- Console logs for error details
- GitHub API rate limit status
The local-first approach provides a robust, user-friendly editing experience that prioritizes performance and reliability. By storing changes locally first and syncing on demand, users can edit posts without worrying about network issues or API failures. The system is designed to be intuitive while providing powerful features for content management.