GitRead stores all your generated READMEs in Supabase, allowing you to access, edit, and reuse your documentation at any time.
README history
Every successfully generated README is automatically saved to your account.
Saving READMEs
After generation, READMEs are stored via the API:
const saveResponse = await fetch('/api/readme-history', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
repoUrl: trimmedRepoUrl,
readmeContent: data.readme
}),
});
Database schema
READMEs are stored in the generated_readmes table:
await supabaseAdmin
.from('generated_readmes')
.insert({
user_id: userId,
repo_url: repoUrl,
readme_content: readmeContent
})
Fields:
user_id - Clerk user identifier
repo_url - GitHub repository URL
readme_content - Full markdown content
created_at - Timestamp (auto-generated)
Fetching history
Retrieve all your generated READMEs, sorted by date:
const { data } = await supabaseAdmin
.from('generated_readmes')
.select('*')
.eq('user_id', userId)
.order('created_at', { ascending: false });
Results are ordered from newest to oldest.
Your README history loads automatically when you sign in and refreshes after each generation.
History interface
View your previously generated READMEs with a clean UI.
Toggle history
<button
onClick={() => setShowHistory(!showHistory)}
className="px-4 py-2 bg-white rounded-lg shadow-sm"
>
{showHistory ? 'Hide History' : 'Show History'}
</button>
History list
{readmeHistory.map((item) => (
<div key={item.id} className="bg-white rounded-xl shadow-sm p-6">
<div className="flex justify-between items-start">
<div>
<h4 className="font-medium">{item.repo_url}</h4>
<p className="text-sm text-gray-500">
{new Date(item.created_at).toLocaleDateString()} at{' '}
{new Date(item.created_at).toLocaleTimeString()}
</p>
</div>
<button
onClick={() => {
setReadme(item.readme_content);
setRepoUrl(item.repo_url);
setShowHistory(false);
}}
>
View README
</button>
</div>
</div>
))}
Each history item displays:
- Repository URL
- Generation date and time
- View button to load the README
Empty state
{readmeHistory.length === 0 ? (
<p className="text-gray-600 text-center py-8">
No READMEs generated yet
</p>
) : (
// History items
)}
Interactive editor
GitRead includes a dual-mode editor for viewing and editing your READMEs.
View modes
Switch between markdown source and rendered preview:
const [viewMode, setViewMode] = useState<'markdown' | 'preview'>('preview');
Mode switcher
<div className="bg-gray-100 p-1 rounded-lg inline-flex">
<button
onClick={() => setViewMode('preview')}
className={viewMode === 'preview'
? 'bg-white shadow-sm'
: 'text-gray-600'}
>
Preview
</button>
<button
onClick={() => setViewMode('markdown')}
className={viewMode === 'markdown'
? 'bg-white shadow-sm'
: 'text-gray-600'}
>
Markdown
</button>
</div>
Markdown editor
Edit the raw markdown with a full-featured textarea:
{viewMode === 'markdown' ? (
<textarea
value={readme}
onChange={handleMarkdownEdit}
className="w-full h-[500px] font-mono text-sm p-4 bg-gray-50 rounded-lg"
spellCheck="false"
/>
) : (
// Preview mode
)}
Features:
- Monospace font for readability
- 500px height for comfortable editing
- Spell-check disabled for code
- Auto-sync with preview mode
Live editing
Changes update the state immediately:
const handleMarkdownEdit = (e: React.ChangeEvent<HTMLTextAreaElement>) => {
setReadme(e.target.value)
}
Edits are local only. To save changes permanently, copy the markdown and commit it to your repository.
Preview mode
Render markdown with GitHub-flavored formatting:
import ReactMarkdown from 'react-markdown'
import rehypeRaw from 'rehype-raw'
<div className="prose prose-gray max-w-none">
<ReactMarkdown rehypePlugins={[rehypeRaw]}>
{readme}
</ReactMarkdown>
</div>
Supported features:
- Headers, lists, and tables
- Code blocks with syntax highlighting
- Links and images
- HTML elements (via rehypeRaw)
- GitHub badges and embeds
Export options
Download or copy your README for use in your repository.
Copy to clipboard
One-click copying with feedback:
const handleCopy = async () => {
try {
await navigator.clipboard.writeText(readme)
setCopyText('Copied')
setTimeout(() => setCopyText('Copy'), 2000)
} catch (err) {
console.error('Failed to copy:', err)
}
}
The button text changes temporarily to confirm success:
<button onClick={handleCopy}>
<svg className="w-4 h-4">...</svg>
{copyText}
</button>
Download as file
Save the README as a markdown file:
const handleDownload = () => {
const blob = new Blob([readme], { type: 'text/markdown' })
const url = URL.createObjectURL(blob)
const a = document.createElement('a')
a.href = url
a.download = 'README.md'
document.body.appendChild(a)
a.click()
document.body.removeChild(a)
URL.revokeObjectURL(url)
}
This creates a proper README.md file ready for your repository.
<div className="flex gap-2">
<button onClick={handleCopy}>
Copy
</button>
<button onClick={handleDownload}>
Download
</button>
</div>
Both actions are always available, regardless of view mode.
Downloaded files are named README.md by default, ready to commit to your repository root.
Authentication requirements
All history and editor features require sign-in:
if (!isSignedIn) {
return (
<div className="relative">
<div className="opacity-50 blur-sm">
<ReactMarkdown>{readme}</ReactMarkdown>
</div>
<div className="absolute inset-0 flex items-center justify-center">
<button onClick={() => setShowAuthModal(true)}>
Sign In to view
</button>
</div>
</div>
)
}
Unauthenticated users see a blurred preview with a sign-in prompt.
Auto-refresh
History refreshes automatically after generation:
const historyResponse = await fetch('/api/readme-history');
if (historyResponse.ok) {
const historyData = await historyResponse.json();
setReadmeHistory(historyData.history);
}
Initial load
useEffect(() => {
async function fetchHistory() {
if (isSignedIn && userId) {
const response = await fetch('/api/readme-history');
const data = await response.json();
setReadmeHistory(data.history);
}
}
fetchHistory();
}, [isSignedIn, userId]);
History loads when you sign in or when the component mounts.
Storage architecture
READMEs are stored in Supabase PostgreSQL:
Service role access
The API uses elevated permissions to manage history:
const supabaseAdmin = createClient(
process.env.NEXT_PUBLIC_SUPABASE_URL!,
process.env.SUPABASE_SERVICE_ROLE_KEY!
);
This ensures users can only access their own history via authenticated API routes.
Row-level security
Database policies enforce user isolation:
- Users can only read their own READMEs
- Users can only insert READMEs for their own account
- All operations require valid authentication
Never expose your Supabase service role key to the client. All database operations must go through authenticated API routes.
Error handling
Graceful handling of save and fetch errors:
try {
const saveResponse = await fetch('/api/readme-history', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ repoUrl, readmeContent }),
});
if (!saveResponse.ok) {
throw new Error(`Failed to save README: ${saveResponse.statusText}`);
}
} catch (error) {
console.error('Error saving README:', error);
// Generation still succeeds, saving is optional
}
Failed saves don’t prevent README display, but are logged for debugging.