Skip to main content
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.

Export toolbar

<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.

Build docs developers (and LLMs) love