Site Branding & Visual Identity
Comprehensive site branding management including logo uploads, OG image customization, and visual identity configuration.
TinyKit Pro provides a comprehensive branding system for managing your site's visual identity, including logo management, OG image customization, and color theming.
Overview
The branding system is organized across two admin sections:
Site Info (/admin/site-settings?tab=site-info) - Manual Save:
- Configure site identity (name, slogan, description)
- Explicit save button for intentional updates
Site Logo (/admin/site-settings?tab=site-logo) - Manual Save:
- Upload and manage site logo (SVG or PNG)
- Preview and crop functionality
- Automatic storage cleanup
OG Images (/admin/og-images) - Auto-Save:
- Customize OG image colors with instant save
- Upload custom OG images with optimization
- Preview changes in real-time
Site Logo Management
Features
- SVG & PNG Support: Upload vector (SVG) or raster (PNG) logos
- Automatic Storage Management: Old logos deleted when updating
- Unoptimized Display: Uses Next.js
<Image unoptimized />for SVG quality - Multi-location Display: Logo appears in headers, OG images, landing pages
- Preview Before Upload: See how your logo will look before saving
Logo Display Locations
- Site Header: Main navigation, falls back to text if no logo
- OG Images: Included in social media preview images
- Landing Page: Consistent branding across all pages
Uploading a Logo
- Navigate to
/admin/site-settings - Click the "Site Logo" tab
- Click "Upload Site Logo" or drag and drop
- Select SVG or PNG file (max 2MB)
- Crop/adjust (4:1 aspect ratio recommended)
- Click "Save" to upload
Technical Implementation
Database Schema:
siteSettings: defineTable({
// ... other fields
logoStorageId: v.optional(v.id("_storage")), // Site logo storage reference
});Backend Functions:
// Generate upload URL (admin only)
export const generateLogoUploadUrl = mutation({
args: {},
handler: async (ctx) => {
await requireAccess(ctx, { userRole: ["admin"] });
return await ctx.storage.generateUploadUrl();
},
});
// Update logo with automatic cleanup
export const updateLogoId = mutation({
args: { logoStorageId: v.id("_storage") },
handler: async (ctx, { logoStorageId }) => {
await requireAccess(ctx, { userRole: ["admin"] });
const settings = await ctx.db.query("siteSettings").first();
// Delete old logo if exists
if (settings?.logoStorageId) {
await ctx.storage.delete(settings.logoStorageId);
}
await ctx.db.patch(settings._id, { logoStorageId });
},
});
// Remove logo
export const removeLogoImage = mutation({
args: {},
handler: async (ctx) => {
await requireAccess(ctx, { userRole: ["admin"] });
const settings = await ctx.db.query("siteSettings").first();
await ctx.db.patch(settings._id, { logoStorageId: undefined });
if (settings?.logoStorageId) {
await ctx.storage.delete(settings.logoStorageId);
}
},
});Frontend Display:
// Header with unoptimized prop for SVG quality
import Image from "next/image";
export default function Header() {
const { settings } = useSettingsWithState();
return (
<header>
<Link href="/">
{settings?.logoUrl ? (
<Image
src={settings.logoUrl}
alt={settings?.siteName ?? "Logo"}
width={120}
height={40}
unoptimized // CRITICAL: Prevents Next.js optimization for SVG
className="max-h-10 w-auto"
priority
/>
) : (
<h1>{settings?.siteName ?? "TinySaaS"}</h1>
)}
</Link>
</header>
);
}Logo Best Practices
Design Guidelines:
- Use SVG for scalability (vector graphics scale perfectly)
- Follow 4:1 aspect ratio (400x100px recommended)
- Keep it simple for legibility at small sizes
- Consider dark mode with transparent backgrounds
File Optimization:
| Format | Optimization | Target Size |
|---|---|---|
| SVG | Use SVGO, remove metadata | < 50KB |
| PNG | TinyPNG compression, 2x retina | < 100KB |
OG Image System
Features
- Live Preview: Real-time OG image preview with instant updates
- Color Presets: Pre-made color combinations for quick theming
- Custom Colors: Full color picker support with hex validation
- Custom Uploads: Upload branded OG images with cropping
- Format Optimization: Automatic WebP conversion (85% quality)
OG Image Settings
{
ogImageSettings: {
primaryColor: "#9333ea", // Primary brand color
secondaryColor: "#dfd9ec", // Secondary accent color
fontColor: "#3d3c4f", // Text color for OG images
ogImageId: null // Custom uploaded image (if any)
}
}Color Presets
Quick application of professional color schemes:
- Modern Purple: Purple/lavender gradient theme
- Ocean Blue: Blue/cyan professional theme
- Sunset Orange: Orange/red energetic theme
- Forest Green: Green/teal natural theme
- Royal Purple: Deep purple luxury theme
Custom OG Image Upload
Process:
- Upload Interface: Drag & drop or click to browse (5MB max)
- Cropping Tool: Fixed 1200:630 aspect ratio
- Optimization: Automatic WebP conversion, 85% quality
- Storage Cleanup: Old images automatically deleted
HTTP Endpoints
/og-settings - Branding Data:
{
"siteName": "TinyKit Pro",
"slogan": "Team collaboration made simple",
"description": "Modern collaboration platform...",
"logoUrl": "https://convex-storage.../logo.svg",
"primaryColor": "#9333ea",
"secondaryColor": "#dfd9ec",
"fontColor": "#3d3c4f"
}/og-image-url - Custom Image Check:
{
"imageUrl": "https://convex-storage.com/image-id" | null
}OG Image Generation Flow
// 1. Check for custom uploaded image
async function getCustomOGImage() {
const response = await fetch(`${CONVEX_SITE_URL}/og-image-url`);
const { imageUrl } = await response.json();
if (imageUrl) {
return fetch(imageUrl); // Return custom image directly
}
return null;
}
// 2. Generate dynamic image if no custom image
async function generateOGImage() {
const response = await fetch(`${CONVEX_SITE_URL}/og-settings`);
const branding = await response.json();
return new ImageResponse(/* JSX with branding data */);
}
// 3. Main OG image route
export default async function Image() {
const customImage = await getCustomOGImage();
return customImage || generateOGImage();
}Database Schema
Complete branding configuration in siteSettings:
{
// Core site identity
siteName: string;
slogan: string;
description: string;
// Logo storage
logoStorageId?: Id<"_storage">;
// OG image configuration
ogImageSettings: {
primaryColor?: string;
secondaryColor?: string;
fontColor?: string;
ogImageId?: Id<"_storage">;
};
}Performance Metrics
File Size Optimization
| Stage | Original | Optimized | Savings |
|---|---|---|---|
| Original PNG | 2-5MB | - | - |
| After Crop | 1-3MB | ~40% | |
| WebP Conversion | 200-800KB | ~80% | |
| Final Result | - | 60-80% | Total |
Load Performance
- Upload Time: ~2-5 seconds for typical images
- Preview Update: < 500ms with debouncing
- OG Generation: < 1 second on Edge Runtime
- Cache Headers: 1-hour caching for production
Access Control
All branding functionality requires admin permissions:
- Upload Images: Only admins can upload logos/OG images
- Remove Images: Only admins can delete images
- Storage Access: Upload URLs generated with permission checks
- Database Updates: All mutations use
requireAccess()with admin role
Integration Points
Theme System
The branding system integrates with the theme system:
- Theme Application: Themes can update OG image colors
- Color Presets: Shared presets between themes and branding
- Consistent Branding: OG images reflect current theme colors
Email Templates
Site branding affects email templates:
- Site Name: Used in email headers and footers
- Logo: Included in email headers
- Colors: Can influence email theming
Best Practices
Image Guidelines
Logos:
- SVG for scalability and smaller file sizes
- 4:1 aspect ratio for headers
- Transparent backgrounds for flexibility
OG Images:
- 1200x630px dimensions for social platforms
- High contrast for visibility
- Test on multiple platforms before finalizing
Brand Consistency
- Choose harmonious colors
- Ensure text readability against backgrounds
- Test social media previews before deploying
Troubleshooting
Logo not displaying
- Check Convex dev server is running
- Verify function is deployed
- Check browser console for 404 errors
Upload fails
- Check file size (2MB for logos, 5MB for OG)
- Verify file format (SVG/PNG for logos)
- Confirm admin permissions
Logo looks corrupted
Ensure unoptimized prop is used on Next.js Image component:
<Image src={logoUrl} unoptimized />Testimonials Management
TinyKit Pro includes a flexible testimonials system that supports both Twitter/X tweet embeds and custom testimonials, perfect for showcasing social proof on...
Site Banner System
TinyKit Pro includes a comprehensive site banner system for displaying promotional announcements, maintenance notices, and important updates across the platf...