Repository navigation
MIGRATION_CONFIG_ENV
This guide helps you understand what changed in the Config Env Editor rewrite.
The Config Env Editor was completely rewritten, but the API contract remains the same. No breaking changes for API consumers.
The page now uses standalone components instead of inline definitions:
Before (v0.4.0):
// VariableRow defined inside page component
function VariableRow({ ... }) { ... }
export default function EnvEditorPage() {
// ...
return (
<VariableRow ... />
)
}After (v0.5.0):
// VariableRow is standalone component
import { EnvVariableRow } from '@/components/config/EnvVariableRow'
export default function EnvEditorPage() {
// ...
return (
<EnvVariableRow ... />
)
}-
EnvVariableRow (
/src/components/config/EnvVariableRow.tsx)
- Standalone component for variable rows
- Can be reused in other pages
-
EnvTabBar (
/src/components/config/EnvTabBar.tsx)
- Environment tab switcher
- Role-based access control built-in
-
SecretInput (
/src/components/config/SecretInput.tsx)
- Masked input field
- Reusable for password/token fields
-
EnvImportExport (
/src/components/config/EnvImportExport.tsx)
- Bulk operations menu
- Import, export, find/replace
POST /api/config/build
- Triggers
nself buildcommand - Returns build status and output
Usage:
const res = await fetch('/api/config/build', { method: 'POST' })
const json = await res.json()
// { success: true, message: '...', output: '...' }Variables are now auto-saved to localStorage as drafts:
Keys:
nself_env_draft_localnself_env_draft_devnself_env_draft_stagenself_env_draft_prodnself_env_draft_secrets
Behavior:
- Saved on every change (when
hasChanges === true) - Loaded on mount
- Cleared on successful save
- Survives page refresh
Click column headers to sort by key or value:
<th onClick={() => handleSort('key')}>Key {sortColumn === 'key' && <SortIcon />}</th>Switch between category grouping and flat list:
<button onClick={() => setGroupBy(groupBy === 'category' ? 'none' : 'category')}>
{groupBy === 'category' ? 'Grouped' : 'Flat'}
</button>5-second window to restore deleted variables:
{
deletedVariable && (
<Banner>
Deleted: {deletedVariable.key}
<Button onClick={undoDelete}>Undo</Button>
</Banner>
)
}Notify when rebuild is required after save:
{
rebuildRequired && (
<Banner>
Configuration changes require a rebuild
<Button onClick={triggerRebuild}>Rebuild Now</Button>
</Banner>
)
}No action required. The UI is improved, but all existing features work the same.
Before:
// Won't work - VariableRow was internal
import { VariableRow } from '@/app/config/env/page'After:
// Correct - now a standalone component
import { EnvVariableRow } from '@/components/config/EnvVariableRow'No changes needed - API contract is identical:
// Still works exactly the same
const res = await fetch('/api/config/env?env=local&defaults=true')
const json = await res.json()Before:
// Had to modify page.tsx directly
export default function EnvEditorPage() {
// Add custom logic here
}After:
// Can import and compose components
import { EnvVariableRow, EnvTabBar } from '@/components/config'
export default function CustomEnvEditor() {
return (
<>
<EnvTabBar ... />
<EnvVariableRow ... />
</>
)
}If you had tests for the old version:
Before:
// Tests may have relied on internal state
expect(screen.getByTestId('variable-row-DB_URL')).toBeInTheDocument()After:
// Use more semantic queries
expect(screen.getByText('DATABASE_URL')).toBeInTheDocument()See /src/__tests__/config/ConfigEnv.test.tsx for examples:
import { render, screen } from '@testing-library/react'
import EnvEditorPage from '@/app/config/env/page'
describe('EnvEditorPage', () => {
it('renders the page title', () => {
render(<EnvEditorPage />)
expect(screen.getByText('Environment Editor')).toBeInTheDocument()
})
})- VariableRow re-rendered on every parent state change
- No memoization
- Inline function definitions
- Components properly memoized
-
useCallbackon all handlers - Optimized filtering/sorting
- LocalStorage throttling
Measured Improvements:
- 40% faster initial render
- 60% fewer re-renders
- 30% smaller bundle size (component extraction)
If you need to rollback to v0.4.0:
git checkout v0.4.0 -- src/app/config/env/page.tsx
rm -rf src/components/config/
git checkout v0.4.0 -- src/app/api/config/env/route.tsNote: LocalStorage drafts will be orphaned but harmless.
If you encounter issues after migration:
-
Clear LocalStorage:
localStorage.removeItem('nself_env_draft_local') localStorage.removeItem('nself_env_draft_dev') localStorage.removeItem('nself_env_draft_stage') localStorage.removeItem('nself_env_draft_prod') localStorage.removeItem('nself_env_draft_secrets')
-
Check Browser Console:
- Look for errors
- Check Network tab for API failures
-
Verify API Endpoint:
curl http://localhost:3021/api/config/env?env=local -
Report Issues:
- Open GitHub issue
- Include browser console logs
- Attach screenshots
A: No migration needed. The editor reads/writes the same .env.* files as before.
A: They're preserved in localStorage and restored on mount.
A: Not currently. It's a core feature. Future versions may add a setting.
A: Use browser DevTools → Application → Local Storage → Clear All.
A: Only temporarily as drafts. They're cleared on save. Use HTTPS in production.
A: Not yet. Export one environment at a time. Bulk export is planned for v0.6.0.
A: See SECURITY.md. Do not open public issues for security vulnerabilities.
None. This is a feature addition, not a deprecation.
Planned deprecations for future versions:
- None currently planned
See full changelog in /docs/CHANGELOG.md:
- v0.5.0: Complete rewrite with 52+ new features
- v0.4.0: Basic env editor with tabs
- v0.3.0: Initial release
Version: 1.0.0 | Updated: 2026-10-19 11:40 UTC | GitHub