user-generated-content
User-Generated Content
Overview
User-generated content (UGC) — customer photos, Q&A, and recent purchase signals — increases product page conversion by giving new visitors authentic proof that real customers use and love the product. Review apps like Judge.me and Yotpo include UGC photo collection, product Q&A, and social proof widgets out of the box. Dedicated UGC platforms like Okendo, Loox, and Bazaarvoice provide more advanced sourcing from Instagram and TikTok. Only build a custom UGC pipeline if your moderation logic, rights management, or database requirements exceed what these tools support.
When to Use This Skill
- When product pages need authentic customer lifestyle photos beyond studio shots
- When building a Q&A section so customers can answer each other's questions
- When displaying "X people bought this in the last 24 hours" urgency signals
- When sourcing Instagram UGC tagged with your brand hashtag for the product page gallery
- When a third-party UGC platform is too expensive for the current scale
Core Instructions
Step 1: Determine platform and choose the right UGC tool
| Platform | Recommended Tool | Why |
|---|---|---|
| Shopify | Loox | Focused on photo reviews — sends post-purchase emails requesting photo uploads, displays a visual gallery on the PDP |
| Shopify | Okendo | Full UGC suite: photo + video reviews, Q&A, attributes ratings (sizing, quality), Instagram UGC sourcing |
| Shopify | Judge.me | Includes photo reviews in the free plan; good starting point before moving to Loox or Okendo |
| WooCommerce | WooCommerce built-in reviews + WP Product Review | Handles text + star reviews; add a media upload plugin for photo submissions |
| WooCommerce | Yotpo for WooCommerce | Full UGC including photo reviews, Q&A, and social proof |
| BigCommerce | Okendo or Yotpo | Both available on the BigCommerce App Marketplace |
| Custom / Headless | Build UGC API with S3 + moderation | Required when UGC data needs to live in your own database or integrate with a custom recommendation engine |
Step 2: Platform-specific setup
Shopify
Option A: Loox (recommended for photo-first UGC)
- Install Loox from the Shopify App Store
- Configure post-purchase photo request:
- Go to Loox → Emails → Photo Request Email
- Set send delay to 7 days after fulfillment
- Offer a discount incentive (e.g., 10% off next order) for submitting a photo review — Loox shows this offer in the request email
- Configure the photo gallery widget:
- Go to Loox → Widgets → Product Widget
- Enable the photo carousel on your product template
- Set minimum photo count to show (only show gallery once you have 3+ photos per product)
- Moderation:
- Go to Loox → Reviews → Pending to review and approve submitted photos
- Enable Auto-publish for verified purchase photo reviews
Option B: Okendo (full UGC including Q&A and Instagram)
- Install Okendo from the App Store
- Configure review forms to collect custom attributes: sizing, quality, value — these appear as filtered search options in the review widget
- Set up Q&A in Okendo → Features → Q&A:
- Enable customer-to-customer Q&A below reviews
- Configure email notifications to your team when a new question is submitted
- Set up an auto-email notification to the question author when staff answers
- Connect Instagram in Okendo → Channels → Instagram:
- Connect your brand Instagram account
- Okendo imports posts tagged with your hashtag and lets you request rights from the poster
- Only publish to product pages after rights are granted
Social proof notification widget (Loox and Okendo): Both apps include a "recent purchases" pop-up widget. Enable it in Loox → Widgets → Social Proof or Okendo → Widgets → Purchase Activity. Configure to show on product and collection pages, not on checkout.
WooCommerce
Yotpo for WooCommerce:
- Install Yotpo Reviews from the WordPress plugin directory
- Yotpo handles photo review requests, moderation, and gallery display
- Configure post-purchase photo request timing in Yotpo → Settings → Review Requests
- Enable Q&A under Yotpo → Features → Q&A
WooCommerce native reviews + photo upload:
- Enable product reviews in WooCommerce → Settings → Products → Reviews
- Install WooCommerce Product Reviews Pro extension to add:
- Photo upload field in the review form
- Verified purchase gating
- Upvote/helpful vote on individual reviews
- For Q&A separately, install WooCommerce Product Q&A from WooThemes or a compatible plugin
Sourcing Instagram UGC: Use Tagembed or Smash Balloon Instagram Feed (WordPress plugins) to embed tagged Instagram posts on product pages. These pull posts by hashtag or mentions and let you curate which appear on each product.
BigCommerce
Okendo for BigCommerce:
- Go to Apps → Search "Okendo" and install
- Configuration is the same as the Shopify version — photo reviews, Q&A, and Instagram sourcing work identically
- Okendo connects to BigCommerce orders for verified purchase gating automatically
Yotpo for BigCommerce:
- Install from the BigCommerce App Marketplace
- Includes photo reviews, Q&A, and SMS review requests
- Yotpo's visual UGC tab imports Instagram and TikTok tagged content for use in the product gallery
Custom / Headless
For headless storefronts needing UGC in your own database:
// lib/ugc.ts
// Generate a presigned S3 URL for direct browser-to-S3 photo uploads
// Never route file uploads through your API server
export async function getPhotoUploadUrl(req: Request, res: Response) {
const { productId, fileName, contentType } = req.body;
if (!['image/jpeg', 'image/png', 'image/webp'].includes(contentType)) {
return res.status(400).json({ error: 'Only JPEG, PNG, and WebP accepted' });
}
const key = `ugc/pending/${productId}/${req.session.customerId}/${Date.now()}-${fileName}`;
const command = new PutObjectCommand({ Bucket: process.env.S3_BUCKET, Key: key, ContentType: contentType });
const uploadUrl = await getSignedUrl(s3, command, { expiresIn: 300 });
await db.ugcPhotos.create({ data: { productId, customerId: req.session.customerId, s3Key: key, status: 'pending_upload' } });
res.json({ uploadUrl, key });
}
// S3 ObjectCreated trigger: run content moderation then resize
export async function processUGCPhoto(s3Key: string) {
const ugcRecord = await db.ugcPhotos.findFirst({ where: { s3Key } });
if (!ugcRecord) return;
// AWS Rekognition content moderation — auto-reject NSFW content
const moderationResult = await rekognition.detectModerationLabels({
Image: { S3Object: { Bucket: process.env.S3_BUCKET!, Name: s3Key } },
MinConfidence: 75,
});
if ((moderationResult.ModerationLabels ?? []).length > 0) {
await db.ugcPhotos.update({ where: { id: ugcRecord.id }, data: { status: 'rejected' } });
return;
}
// Resize to thumbnail (200px), medium (600px), full (1200px) WebP variants
const original = await s3.getObject({ Bucket: process.env.S3_BUCKET!, Key: s3Key });
const buffer = Buffer.from(await original.Body!.transformToByteArray());
for (const { suffix, width } of [{ suffix: 'thumbnail', width: 200 }, { suffix: 'medium', width: 600 }, { suffix: 'full', width: 1200 }]) {
const resized = await sharp(buffer).resize(width).webp({ quality: 80 }).toBuffer();
await s3.putObject({
Bucket: process.env.S3_BUCKET!, Body: resized, ContentType: 'image/webp',
Key: `ugc/approved/${ugcRecord.productId}/${ugcRecord.id}/${suffix}.webp`,
});
}
// Auto-approve verified purchase photos; queue others for manual review
const isVerifiedPurchase = await db.orderItems.findFirst({ where: { customerId: ugcRecord.customerId, productId: ugcRecord.productId } });
await db.ugcPhotos.update({ where: { id: ugcRecord.id }, data: {
status: isVerifiedPurchase ? 'approved' : 'pending_review',
verifiedPurchase: !!isVerifiedPurchase,
}});
}
// Product Q&A — submit and answer questions
export async function submitQuestion(req: Request, res: Response) {
const { productId, question, authorName } = req.body;
const q = await db.productQuestions.create({ data: { productId, question, authorName, authorEmail: req.session.customerEmail, status: 'pending' } });
await notifyProductTeam({ questionId: q.id, productId, question });
res.json({ questionId: q.id });
}
// Social proof feed: recent purchases cached for 5 minutes
export async function buildRecentPurchaseFeed() {
const recentOrders = await db.orders.findMany({
where: { createdAt: { gte: new Date(Date.now() - 86400000) }, status: 'completed' },
include: { lineItems: { include: { product: true } }, customer: true },
orderBy: { createdAt: 'desc' },
take: 100,
});
const feed = recentOrders.flatMap(order =>
order.lineItems.slice(0, 1).map(item => ({
productId: item.productId,
productName: item.product.name,
buyerFirstName: order.customer.firstName,
buyerLocation: order.customer.city ?? 'somewhere',
purchasedAt: order.createdAt.toISOString(),
}))
);
await redis.setex('social_proof_feed', 300, JSON.stringify(feed));
}
Step 3: Configure rights management for Instagram UGC
Before displaying any customer social media content on your store or in paid ads:
- Request rights explicitly — Okendo and Yotpo have built-in rights request flows that DM the Instagram user asking for permission
- Never use Instagram content in paid advertising without written consent — this creates legal liability
- Track rights status per photo — only display content where rights are confirmed; Okendo and Yotpo track this automatically
- For organic sourcing (no dedicated app), comment on the post or send a DM with a rights request and keep a record of the response
Step 4: Set up Q&A moderation
Unanswered questions damage trust more than having no Q&A at all:
In Okendo and Yotpo:
- Configure a daily email digest to your team listing all unanswered questions
- Set a 48-hour SLA for staff answers — questions older than 7 days without answers should be escalated
Best practice: Enable customer-to-customer answers (other buyers can answer) — this reduces the staff burden and customers often provide more authentic answers than support agents.
Step 5: Measure UGC impact
| Metric | Benchmark | Where to Find |
|---|---|---|
| UGC photo submission rate | 2–5% of delivered orders | Loox / Okendo email analytics |
| Products with 3+ customer photos | Track % of top 50 products | App dashboard |
| Q&A answer rate | 90%+ within 7 days | Okendo / Yotpo Q&A report |
| Social proof widget click-through | 1–3% of product page visitors | Widget analytics in Loox/Okendo |
Best Practices
- Use Loox or Okendo before building custom — they handle S3 storage, content moderation, rights management, and Instagram import; building this from scratch takes 2–4 weeks
- Offer a small incentive for photo submissions — Loox's discount-for-photo feature increases submission rates by 3–5x compared to asking without an incentive
- Auto-approve verified purchase photos — these have lower risk and removing the manual review bottleneck dramatically increases UGC volume
- Show verified purchase badges on UGC photos — customers distinguish genuine-use photos from staged ones; the badge increases trust
- Always run AI content moderation before display — never approve photos without at least automated screening; AWS Rekognition or Google Vision SafeSearch catch NSFW content automatically
- Obtain explicit rights before using UGC in paid ads — using customer photos in advertising without written consent creates legal liability
Common Pitfalls
| Problem | Solution |
|---|---|
| Inappropriate content appears on product pages | Enable content moderation before approval; Loox and Okendo run automated screening automatically |
| Social proof widget shows stale or fake urgency | Use a 5-minute cache and only show genuine purchase signals — fabricating data creates reputational and legal risk |
| Q&A section has unanswered questions for weeks | Send a daily digest to the product team; questions older than 7 days without a staff answer damage trust more than no Q&A |
| UGC photos slow page LCP | Loox and Okendo serve via CDN with automatic resizing; for custom builds, always resize to 200px thumbnails and use lazy loading |
| Instagram posts used in paid ads without rights | Track rights approval status per post; Okendo handles this automatically — never publish to ads without confirmed consent |
Related Skills
- @product-reviews-ratings
- @personalization-engine
- @customer-segmentation