by InlinexDev

Building a Shopify App with Remix and React: Lessons Learned

Practical lessons from building LogoBadge, a Shopify app that displays Google Reviews badges, using Shopify's Remix framework.

ShopifyRemixReactShopify appGoogle Reviews

Why Shopify Moved to Remix

Shopify's app development framework has evolved significantly. The current recommended approach uses Remix with React, replacing the older Next.js-based template. LogoBadge, our Google Reviews badge app, was built entirely on this new stack.

What LogoBadge Does

LogoBadge adds a Google Reviews badge directly under a Shopify store's logo. When customers see a real Google rating on every page, it builds trust instantly. The app:

  • Fetches review data from the Google Places API
  • Renders a customizable badge via Liquid theme extensions
  • Stores merchant settings in Prisma/SQLite (development) or PostgreSQL (production)

Setting Up the Shopify Remix App

Shopify provides a CLI to scaffold the app:

npx @shopify/create-app@latest

This generates a Remix app with:

  • OAuth authentication built in
  • Session management via Prisma
  • App Bridge integration for the admin interface
  • Webhook handling boilerplate

Project Structure

app/
  routes/
    app._index.tsx    # Main dashboard
    app.settings.tsx  # Settings page
    webhooks.tsx      # Webhook handler
  components/
    BadgePreview.tsx
    SettingsForm.tsx
extensions/
  logo-badge/
    blocks/
      badge.liquid     # Theme extension block
    locales/
      en.default.json
prisma/
  schema.prisma

Theme App Extensions

The badge itself lives as a Theme App Extension — a Liquid block that merchants can place in their theme using the customizer. No code injection or theme editing required.

{% comment %} blocks/badge.liquid {% endcomment %}
{% if block.settings.place_id != blank %}
  <div class="logobadge-container" 
       style="text-align: {{ block.settings.alignment }};">
    <a href="{{ block.settings.review_url }}" 
       target="_blank" rel="noopener">
      <div class="logobadge-badge">
        <img src="{{ 'google-logo.svg' | asset_url }}" 
             alt="Google" width="16" height="16">
        <span class="logobadge-rating">
          {{ block.settings.rating }}
        </span>
        <span class="logobadge-stars">
          {% for i in (1..5) %}
            {% if i <= block.settings.rating %}
              ★
            {% else %}
              ☆
            {% endif %}
          {% endfor %}
        </span>
        <span class="logobadge-count">
          ({{ block.settings.review_count }})
        </span>
      </div>
    </a>
  </div>
{% endif %}

Google Places API Integration

Fetching review data requires the Places API (New):

async function fetchGoogleReviews(placeId: string) {
  const response = await fetch(
    `https://places.googleapis.com/v1/places/${placeId}`,
    {
      headers: {
        'X-Goog-Api-Key': process.env.GOOGLE_PLACES_API_KEY!,
        'X-Goog-FieldMask': 'rating,userRatingCount,googleMapsUri'
      }
    }
  );

  const data = await response.json();
  return {
    rating: data.rating,
    reviewCount: data.userRatingCount,
    mapsUrl: data.googleMapsUri
  };
}

Prisma for Data Persistence

Merchant settings are stored using Prisma:

model BadgeSettings {
  id          String   @id @default(cuid())
  shop        String   @unique
  placeId     String
  rating      Float    @default(0)
  reviewCount Int      @default(0)
  alignment   String   @default("center")
  lastSynced  DateTime @default(now())
  createdAt   DateTime @default(now())
  updatedAt   DateTime @updatedAt
}

Key Lessons from Building with Shopify Remix

1. App Bridge Is Mandatory

Shopify requires embedded apps to use App Bridge for navigation, modals, and toasts. Don't fight it — embrace the Polaris design system.

2. Webhook Verification Is Critical

Shopify signs webhook payloads with HMAC. Always verify:

import crypto from 'crypto';

function verifyWebhook(body: string, hmacHeader: string) {
  const hash = crypto
    .createHmac('sha256', process.env.SHOPIFY_API_SECRET!)
    .update(body)
    .digest('base64');
  return crypto.timingSafeEqual(
    Buffer.from(hash),
    Buffer.from(hmacHeader)
  );
}

3. Scopes Should Be Minimal

Request only the API scopes you actually need. LogoBadge only needs read_themes and write_themes — nothing more. Merchants are wary of apps that request excessive permissions.

4. Testing Requires a Development Store

You can't test Shopify apps locally against production stores. Create a development store through your Partner account and test everything there first.

Deployment

Shopify apps need a publicly accessible URL. Railway provides:

  • Automatic HTTPS (required by Shopify)
  • PostgreSQL for production database
  • Environment variable management
  • Simple deployments via GitHub integration

Conclusion

The Shopify Remix stack is opinionated but productive. Once you understand the conventions — App Bridge, Polaris, Theme Extensions, and webhook handling — you can build and ship apps quickly. LogoBadge went from concept to published in the Shopify App Store in under two weeks.

Related Project

LogoBadge - Google Reviews

Shopify app that automatically displays a Google Reviews badge directly under the store logo, showing live star ratings and review counts from Google Business Profile.