TinyKit Pro Docs

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

  1. Site Header: Main navigation, falls back to text if no logo
  2. OG Images: Included in social media preview images
  3. Landing Page: Consistent branding across all pages
  1. Navigate to /admin/site-settings
  2. Click the "Site Logo" tab
  3. Click "Upload Site Logo" or drag and drop
  4. Select SVG or PNG file (max 2MB)
  5. Crop/adjust (4:1 aspect ratio recommended)
  6. 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:

FormatOptimizationTarget Size
SVGUse SVGO, remove metadata< 50KB
PNGTinyPNG 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:

  1. Upload Interface: Drag & drop or click to browse (5MB max)
  2. Cropping Tool: Fixed 1200:630 aspect ratio
  3. Optimization: Automatic WebP conversion, 85% quality
  4. 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

StageOriginalOptimizedSavings
Original PNG2-5MB--
After Crop1-3MB~40%
WebP Conversion200-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

  1. Check Convex dev server is running
  2. Verify function is deployed
  3. Check browser console for 404 errors

Upload fails

  1. Check file size (2MB for logos, 5MB for OG)
  2. Verify file format (SVG/PNG for logos)
  3. Confirm admin permissions

Logo looks corrupted

Ensure unoptimized prop is used on Next.js Image component:

<Image src={logoUrl} unoptimized />

← Previous: Mailing List | Next: Email Templates →

On this page

Ship your startup faster. In minutes.

Get TinyKit Pro