A product can have great features but still lose users in the first thirty seconds. New users land on an empty Record page, see a button, and wonder: what happens next? Where do my videos go? Can anyone see them?
We shipped SendRec v1.52.0 with four changes focused on growth and adoption: onboarding guides for new users, a robots.txt for search engine crawling, enforced branding for free-tier users, and a bug fix where deleted videos still counted toward the monthly quota.
Onboarding empty states
When a new user has zero videos, the Record, Upload, and Library pages now show a simple 3-step guide:
- Record your screen or upload a video
- Share the link with anyone
- Track views and get feedback
The detection is straightforward — we already have a /api/videos/limits endpoint that returns videosUsedThisMonth. If it's zero, show the guide:
{limits && limits.videosUsedThisMonth === 0 && (
<div style={{ maxWidth: 400, margin: "0 auto 24px", padding: "20px 24px", background: "var(--color-surface)", borderRadius: 12 }}>
<p style={{ fontWeight: 600, marginBottom: 12 }}>
Get started in 3 steps
</p>
<div>1. Record your screen or upload a video</div>
<div>2. Share the link with anyone</div>
<div>3. Track views and get feedback</div>
</div>
)}
No new API calls, no feature flags, no user preferences to track. The guide disappears permanently the moment you upload your first video.
The Library page takes it a step further — the empty state includes both "Record" and "Upload" buttons so new users have clear paths forward instead of staring at "No recordings yet."
robots.txt for watch pages
SendRec watch pages already have OpenGraph tags, Twitter Cards, VideoObject JSON-LD, and canonical URLs. But without a robots.txt, search engines had no guidance on what to crawl.
We added a GET /robots.txt handler:
func (s *Server) handleRobotsTxt(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "text/plain; charset=utf-8")
_, _ = w.Write([]byte("User-agent: *\nAllow: /watch/\nAllow: /embed/\nDisallow: /\n"))
}
This allows crawling of public watch and embed pages (where the SEO value is) while blocking the SPA routes, API endpoints, and authentication pages. Simple and effective.
Enforced "Shared via SendRec" badge
SendRec has custom branding — Pro users can set their own logo, colors, footer text, and CSS. But free-tier users were also able to remove the "Shared via SendRec" link from the watch page footer by setting custom footer text.
Now the watch page template checks the user's subscription plan. Free users always see "Shared via SendRec" appended to any custom footer text:
{{if eq .SubscriptionPlan "pro"}}
{{if .Branding.FooterText}}<p>{{.Branding.FooterText}}</p>{{end}}
{{else}}
<p>{{if .Branding.FooterText}}{{.Branding.FooterText}} · {{end}}Shared via <a href="https://sendrec.eu">SendRec</a></p>
{{end}}
Pro users keep full control over their footer. The same logic applies to both the watch page and the embed player.
The implementation required threading the user's subscription_plan column (already in the database from the billing migration) through to the watch page template data.
Deleted videos still counted toward quota
This was a bug. The monthly video count query was:
SELECT COUNT(*) FROM videos
WHERE user_id = $1 AND created_at >= date_trunc('month', now())
SendRec uses soft deletes — DELETE /api/videos/:id sets status = 'deleted' rather than removing the row. Every other query in the codebase filters status != 'deleted', but this count query didn't. So deleting videos had no effect on the monthly limit counter.
The fix is one clause:
SELECT COUNT(*) FROM videos
WHERE user_id = $1 AND created_at >= date_trunc('month', now())
AND status != 'deleted'
We also added a client-side fix: the Library page now decrements the "N / 25 videos this month" counter immediately after a successful delete, instead of requiring a page refresh.
Try it
All changes are live at app.sendrec.eu. SendRec is open source (AGPL-3.0) — the watch page template is in watch_page.go and the onboarding guides in Record.tsx, Upload.tsx, and Library.tsx.
Top comments (0)