How to Create City Boy Meme




How to Create City Boy Meme with Next.js 16 & Fabric.js

Ever wondered how to build a web app that goes viral? I recently launched city boy meme – a free, lightning-fast meme maker that’s been creating 2,000+ memes daily. Here’s how I built it and the lessons I learned along the way.



🎯 The Problem

Existing meme generators are slow, bloated with ads, add watermarks, or require signups. I wanted to build something different:

  • Instant loading – No waiting, no spinners
  • 🎨 Full customization – Fonts, colors, text styles
  • 💯 Zero friction – No signup, no watermarks, no BS
  • 📱 Mobile-first – Works perfectly on all devices



🛠️ Tech Stack

After evaluating several options, I settled on:


  "framework": "Next.js 16",
  "styling": "Tailwind CSS 4",
  "canvas": "Fabric.js 5.3",
  "language": "TypeScript",
  "deployment": "Vercel"

Enter fullscreen mode

Exit fullscreen mode



Why Next.js 16?

Next.js 16 brings some game-changing features:

  • App Router – Better performance and SEO
  • Server Components – Reduced JavaScript bundle
  • Image Optimization – Automatic WebP conversion
  • Built-in SEO – Metadata API for perfect SEO



Why Fabric.js?

I initially tried HTML Canvas API directly, but Fabric.js saved me weeks:

  • Object-based canvas manipulation
  • Built-in text editing and dragging
  • Easy export to PNG/JPEG
  • Great TypeScript support



🏗️ Architecture Overview

cityboymeme/
├── app/
│   ├── layout.tsx          # Root layout with SEO
│   ├── page.tsx            # Home page
│   ├── sitemap.ts          # Dynamic sitemap
│   └── globals.css         # Global styles
├── components/
│   ├── MemeEditor.tsx      # Main editor component
│   └── FabricCanvas.tsx    # Canvas wrapper
└── public/
    ├── logo.png            # Meme template
    └── robots.txt          # SEO config
Enter fullscreen mode

Exit fullscreen mode



💻 Building the Meme Editor



1. Canvas Component with Fabric.js

The core of the app is a custom canvas component that wraps Fabric.js:

// components/FabricCanvas.tsx
'use client'

import  useEffect, useRef, forwardRef, useImperativeHandle  from 'react'
import  fabric  from 'fabric'

export interface TextElement 
  id: string
  text: string
  color: string
  font: string
  style: 'bold' 

export interface FabricCanvasRef 
  addText: (element: TextElement) => void
  updateText: (id: string, updates: Partial<TextElement>) => void
  removeText: (id: string) => void
  exportAsDataURL: () => string 

interface Props  null) => void
  onCanvasReady: () => void


const FabricCanvas = forwardRef<FabricCanvasRef, Props>(
  ( backgroundImage, width, height, onTextSelect, onCanvasReady , ref) => 
    const canvasRef = useRef<HTMLCanvasElement>(null)
    const fabricRef = useRef<fabric.Canvas 
)

FabricCanvas.displayName = 'FabricCanvas'
export default FabricCanvas
Enter fullscreen mode

Exit fullscreen mode



2. Main Editor Component

The editor manages state and provides the UI:

// components/MemeEditor.tsx (simplified)
'use client'

import  useState, useRef  from 'react'
import FabricCanvas,  FabricCanvasRef, TextElement  from './FabricCanvas'

export default function MemeEditor() 
  const [textElements, setTextElements] = useState<TextElement[]>([
    
      id: '1',
      text: 'City Boys Be Like',
      color: '#FFFFFF',
      font: 'Impact',
      style: 'outlined',
      size: 48
    
  ])
  const [selectedTextId, setSelectedTextId] = useState<string 
Enter fullscreen mode

Exit fullscreen mode



🚀 Performance Optimizations



1. Image Optimization

Next.js Image component automatically optimizes images:

import Image from 'next/image'

<Image
  src="/logo.png"
  alt="City Boy Meme template"
  width=600
  height=600
  priority // Load immediately for LCP
/>
Enter fullscreen mode

Exit fullscreen mode



2. Code Splitting

Fabric.js is large (~500KB), so I lazy-load it:

'use client'

import dynamic from 'next/dynamic'

const FabricCanvas = dynamic(() => import('./FabricCanvas'), 
  ssr: false, // Fabric.js requires window object
  loading: () => <div>Loading canvas...</div>
)
Enter fullscreen mode

Exit fullscreen mode



3. Lighthouse Score: 95+

After optimizations:

  • Performance: 98
  • Accessibility: 100
  • Best Practices: 100
  • SEO: 100



🔍 SEO Strategy

SEO was crucial for organic growth. Here’s what worked:



1. Perfect Metadata

// app/layout.tsx
export const metadata: Metadata = 
  title: 'City Boy Meme - Free Online Meme Maker',
  description: 'Create hilarious City Boy memes instantly with our free online generator. Add custom text, choose fonts and colors. Download high-quality memes. No signup!',
  keywords: [
    'city boy meme',
    'meme generator',
    'free meme maker',
    'online meme creator',
  ],
  openGraph: 
    type: 'website',
    url: 'https://cityboymeme.com',
    title: 'City Boy Meme - Free Online Meme Maker',
    description: 'Create viral City Boy memes instantly',
    images: ['/logo.png'],
  ,

Enter fullscreen mode

Exit fullscreen mode



2. Structured Data (Schema.org)

// FAQPage Schema for Featured Snippets
const faqSchema = 
  '@context': 'https://schema.org',
  '@type': 'FAQPage',
  mainEntity: [
    
      '@type': 'Question',
      name: 'What is a City Boy Meme?',
      acceptedAnswer: 
        '@type': 'Answer',
        text: 'The City Boy Meme is a viral internet format featuring a character with an exaggerated shocked expression...'
      
    ,
    // More questions...
  ]

Enter fullscreen mode

Exit fullscreen mode

This helped me get Featured Snippets on Google!



3. Dynamic Sitemap

// app/sitemap.ts
import  MetadataRoute  from 'next'

export default function sitemap(): MetadataRoute.Sitemap 
  return [
    
      url: 'https://cityboymeme.com',
      lastModified: new Date(),
      changeFrequency: 'daily',
      priority: 1,
    ,
    
      url: 'https://cityboymeme.com/what-is-city-boy-meme',
      lastModified: new Date(),
      changeFrequency: 'weekly',
      priority: 0.9,
    ,
  ]

Enter fullscreen mode

Exit fullscreen mode



📊 Results After 2 Weeks

The results exceeded my expectations:

  • 📈 2,000+ memes created daily
  • 🔍 Ranking #3 for “city boy meme” on Google
  • Page load time: <1 second
  • 📱 Mobile traffic: 65%
  • 💯 Zero complaints about performance



🎓 Lessons Learned



1. Start with SEO from Day 1

Don’t treat SEO as an afterthought. I spent 30% of development time on SEO, and it paid off massively.



2. Mobile-First is Non-Negotiable

65% of my users are on mobile. The app works perfectly on phones because I designed for mobile first.



3. Performance = User Retention

Users expect instant loading. Every 100ms delay costs you users. Optimize ruthlessly.



4. Fabric.js > Raw Canvas

I initially tried raw Canvas API. Switching to Fabric.js saved me 2 weeks and resulted in better UX.



5. TypeScript Saves Time

TypeScript caught dozens of bugs before they reached production. The upfront cost is worth it.



🔮 What’s Next?

I’m planning to add:

  • 🎨 More meme templates
  • 📤 Direct social media sharing
  • 🎥 GIF/Video meme support
  • 🌍 Internationalization (i18n)
  • 🤖 AI-powered text suggestions



💡 Key Takeaways

If you’re building a viral web app:

  1. Choose the right tools – Next.js 16 + Fabric.js was perfect for this
  2. Optimize for performance – Users expect instant loading
  3. SEO from day 1 – Organic traffic is the best traffic
  4. Mobile-first design – Most users are on mobile
  5. Keep it simple – No signup, no friction, just value



🔗 Links



🙏 Thanks for Reading!

If you found this helpful, please:

  • ⭐ Star the repo on GitHub
  • 💬 Leave a comment with your questions
  • 🔄 Share with your network
  • 🚀 Try building your own viral app!

What would you build with Next.js 16 and Fabric.js? Drop your ideas in the comments! 👇




📚 Resources


Built with ❤️ using Next.js 16, Fabric.js, and lots of coffee ☕



Source link