Deploying and Optimizing Sitecore Marketplace Apps: Production Best Practices

Share
Deploy and optimize Sitecore Marketplace apps to Vercel, Netlify, and Azure - Part 3 guide
Deploy and optimize Sitecore Marketplace apps to Vercel, Netlify, and Azure - Part 3 guide

We've built our Marketplace app, integrated it with XM Cloud APIs, and tested it locally. Now comes the most critical part—deploying to production and making sure it performs flawlessly for your users.

This final part covers everything from deployment strategies to performance optimization, security best practices, and preparing for the public Marketplace.

Table of Contents

  1. Preparing for Production
  2. Deployment Strategy Overview
  3. Deploying to Vercel
  4. Deploying to Netlify
  5. Deploying to Azure Static Web Apps
  6. Updating Your App Registration
  7. Performance Optimization
  8. Security Best Practices
  9. Monitoring and Analytics
  10. Common Production Issues
  11. Publishing to Public Marketplace
  12. Final Checklist

Preparing for Production

Before deploying, let's make sure your app is production-ready.

Pre-Deployment Checklist

Code Quality:

  • Remove all console.log statements (or use proper logging)
  • Fix all TypeScript errors and warnings
  • Run ESLint and fix issues
  • Test all features thoroughly
  • Handle edge cases and error states

Performance:

  • Optimize images and assets
  • Implement code splitting where needed
  • Remove unused dependencies
  • Test bundle size (npm run build)

Security:

  • No hardcoded secrets or API keys
  • Environment variables properly configured
  • Input validation on all user inputs
  • XSS protection in place

UX/UI:

  • Loading states for all async operations
  • Error messages are user-friendly
  • Responsive design tested on mobile/tablet/desktop
  • Accessibility basics covered (keyboard navigation, ARIA labels)

Environment Variables Setup

Create .env.production:

bash

NEXT_PUBLIC_APP_ENV=production
NEXT_PUBLIC_API_TIMEOUT=10000
NEXT_PUBLIC_SENTRY_DSN=your-sentry-dsn # if using Sentry

Add .env.local to .gitignore:

bash

# .gitignore
.env.local
.env.*.local
node_modules/
.next/

Deployment Strategy Overview

You have three main hosting options:

PlatformBest ForProsCons
VercelNext.js appsZero config, automatic, great DXVendor lock-in
NetlifyStatic sitesEasy, good free tierLess Next.js optimization
AzureEnterpriseFull control, integration with Azure servicesMore complex setup

For this guide, we'll cover all three, but Vercel is recommended for Next.js apps.


Deploying to Vercel

Vercel is built by the creators of Next.js, making it the smoothest option.

Step 1: Push to GitHub

bash

# Initialize git if you haven't
git init

# Add all files
git add .

# Commit
git commit -m "Production ready app"

# Create repository on GitHub, then:
git remote add origin https://github.com/yourusername/sitecore-analytics-app.git
git push -u origin main

Step 2: Deploy to Vercel

  1. Go to vercel.com
  2. Click "New Project"
  3. Import your GitHub repository
  4. Vercel auto-detects Next.js settings:
    • Framework Preset: Next.js
    • Build Command: npm run build
    • Output Directory: .next
  5. Add environment variables:
    • NEXT_PUBLIC_APP_ENV: production
  6. Click "Deploy"

Step 3: Get Your Production URL

After deployment completes (usually 2-3 minutes), you'll get a URL like:

https://sitecore-analytics-app.vercel.app

Vercel-Specific Optimizations

1. Enable Edge Functions (optional)

For global performance, enable edge runtime:

typescript

// src/app/standalone/page.tsx
export const runtime = 'edge'; // Run on the edge

2. Configure Caching

Add to next.config.js:

javascript

/** @type {import('next').NextConfig} */
const nextConfig = {
  images: {
    domains: ['placehold.co'], // Add your image domains
  },
  headers: async () => [
    {
      source: '/:path*',
      headers: [
        {
          key: 'Cache-Control',
          value: 'public, max-age=3600, must-revalidate',
        },
      ],
    },
  ],
};

module.exports = nextConfig;

Deploying to Netlify

Netlify is another excellent option with a generous free tier.

Step 1: Push to GitHub

Same as Vercel—push your code to GitHub.

Step 2: Deploy to Netlify

  1. Go to netlify.com
  2. Click "Add new site""Import an existing project"
  3. Connect your GitHub repository
  4. Configure build settings:
    • Build command: npm run build
    • Publish directory: .next
  5. Add environment variables in Site settings → Environment variables
  6. Click "Deploy site"

Step 3: Configure Next.js on Netlify

Install the Netlify Next.js plugin:

bash

npm install -D @netlify/plugin-nextjs

Create netlify.toml:

toml

[build]
  command = "npm run build"
  publish = ".next"

[[plugins]]
  package = "@netlify/plugin-nextjs"

Redeploy after adding this configuration.


Deploying to Azure Static Web Apps

For enterprise deployments, Azure provides robust infrastructure.

Step 1: Create Azure Static Web App

  1. Log into Azure Portal
  2. Create Static Web App resource
  3. Connect to GitHub repository
  4. Configure build:
    • App location: /
    • API location: (leave empty)
    • Output location: .next

Step 2: Configure GitHub Actions

Azure automatically creates a GitHub Actions workflow. Update it:

yaml

name: Azure Static Web Apps CI/CD

on:
  push:
    branches:
      - main

jobs:
  build_and_deploy_job:
    runs_on: ubuntu-latest
    steps:
      - uses: actions/checkout@v2
      
      - name: Build And Deploy
        uses: Azure/static-web-apps-deploy@v1
        with:
          azure_static_web_apps_api_token: ${{ secrets.AZURE_STATIC_WEB_APPS_API_TOKEN }}
          repo_token: ${{ secrets.GITHUB_TOKEN }}
          action: "upload"
          app_location: "/"
          output_location: ".next"

Step 3: Add Environment Variables

In Azure Portal:

  1. Go to your Static Web App
  2. Navigate to Configuration
  3. Add application settings:
    • NEXT_PUBLIC_APP_ENV: production

Updating Your App Registration

Once deployed, update your app in Developer Studio.

Step 1: Edit App Configuration

  1. Log into Cloud Portal
  2. Go to Developer Studio
  3. Find your app and click "Edit"

Step 2: Update Deployment URL

Change from:

http://localhost:3000/standalone

To your production URL:

https://sitecore-analytics-app.vercel.app/standalone

Replace placeholder with your actual app logo:

  1. Upload a 512x512px PNG to your hosting
  2. Update App Logo URL with the production URL

Step 4: Test Production App

  1. Save changes
  2. Open your app from Cloud Portal navigation
  3. Verify everything works:
    • SDK initializes successfully
    • Data loads correctly
    • No console errors
    • All features functional

Performance Optimization

Let's make sure your app loads fast and runs smoothly.

Bundle Size Optimization

1. Analyze Your Bundle

bash

npm install -D @next/bundle-analyzer

Update next.config.js:

javascript

const withBundleAnalyzer = require('@next/bundle-analyzer')({
  enabled: process.env.ANALYZE === 'true',
});

module.exports = withBundleAnalyzer({
  // your existing config
});

Run analysis:

bash

ANALYZE=true npm run build

2. Code Splitting

Split large components:

typescript

import dynamic from 'next/dynamic';

// Lazy load heavy components
const AnalyticsChart = dynamic(() => import('@/components/AnalyticsChart'), {
  loading: () => <div>Loading chart...</div>,
  ssr: false, // Disable SSR for client-only components
});

3. Optimize Images

Use Next.js Image component:

typescript

import Image from 'next/image';

<Image
  src="/logo.png"
  alt="App Logo"
  width={512}
  height={512}
  priority // For above-fold images
/>

Data Fetching Optimization

1. Implement Caching

typescript

// Simple in-memory cache
const cache = new Map<string, { data: any; timestamp: number }>();

async function fetchWithCache(key: string, fetchFn: () => Promise<any>, ttl = 300000) {
  const cached = cache.get(key);
  
  if (cached && Date.now() - cached.timestamp < ttl) {
    return cached.data;
  }
  
  const data = await fetchFn();
  cache.set(key, { data, timestamp: Date.now() });
  return data;
}

// Usage
const stats = await fetchWithCache('content-stats', async () => {
  const xmc = await initializeXMC(client);
  return xmc.graphQL.query({ query });
}, 300000); // 5 minutes

2. Debounce API Calls

typescript

import { debounce } from 'lodash';

const debouncedSearch = debounce(async (searchTerm: string) => {
  const results = await searchContent(searchTerm);
  setSearchResults(results);
}, 500); // Wait 500ms after user stops typing

Runtime Performance

1. Memoize Expensive Calculations

typescript

import { useMemo } from 'react';

const expensiveCalculation = useMemo(() => {
  return items.reduce((acc, item) => {
    // Complex calculation
    return acc + item.value;
  }, 0);
}, [items]); // Only recalculate when items change

2. Virtual Lists for Large Data

bash

npm install react-window

typescript

import { FixedSizeList } from 'react-window';

function ItemList({ items }) {
  return (
    <FixedSizeList
      height={600}
      itemCount={items.length}
      itemSize={50}
      width="100%"
    >
      {({ index, style }) => (
        <div style={style}>{items[index].name}</div>
      )}
    </FixedSizeList>
  );
}

Security Best Practices

Security is non-negotiable for production apps.

Input Validation

typescript

// Sanitize user input
function sanitizeInput(input: string): string {
  return input
    .trim()
    .replace(/[<>]/g, '') // Remove HTML tags
    .substring(0, 200); // Limit length
}

// Validate before using
function handleUserInput(value: string) {
  const sanitized = sanitizeInput(value);
  
  if (!/^[a-zA-Z0-9\s-]+$/.test(sanitized)) {
    throw new Error('Invalid characters in input');
  }
  
  return sanitized;
}

Content Security Policy

Add to next.config.js:

javascript

const nextConfig = {
  async headers() {
    return [
      {
        source: '/:path*',
        headers: [
          {
            key: 'Content-Security-Policy',
            value: [
              "default-src 'self'",
              "script-src 'self' 'unsafe-inline' 'unsafe-eval'",
              "style-src 'self' 'unsafe-inline'",
              "img-src 'self' data: https:",
              "connect-src 'self' https://*.sitecore.com",
            ].join('; '),
          },
        ],
      },
    ];
  },
};

Rate Limiting

Implement rate limiting for API calls:

typescript

class RateLimiter {
  private requests: number[] = [];
  private limit: number;
  private window: number;

  constructor(limit: number, windowMs: number) {
    this.limit = limit;
    this.window = windowMs;
  }

  async checkLimit(): Promise<boolean> {
    const now = Date.now();
    this.requests = this.requests.filter(time => now - time < this.window);
    
    if (this.requests.length >= this.limit) {
      return false;
    }
    
    this.requests.push(now);
    return true;
  }
}

// Usage
const limiter = new RateLimiter(10, 60000); // 10 requests per minute

async function fetchData() {
  if (!await limiter.checkLimit()) {
    throw new Error('Rate limit exceeded. Please try again later.');
  }
  
  // Make API call
}

Monitoring and Analytics

Track your app's performance and usage.

Error Tracking with Sentry

bash

npm install @sentry/nextjs

Create sentry.client.config.ts:

typescript

import * as Sentry from '@sentry/nextjs';

Sentry.init({
  dsn: process.env.NEXT_PUBLIC_SENTRY_DSN,
  environment: process.env.NEXT_PUBLIC_APP_ENV,
  tracesSampleRate: 1.0,
});

Wrap your app:

typescript

import { ErrorBoundary } from '@sentry/nextjs';

export default function App({ Component, pageProps }: AppProps) {
  return (
    <ErrorBoundary fallback={<ErrorFallback />}>
      <Component {...pageProps} />
    </ErrorBoundary>
  );
}

Custom Analytics

Track user interactions:

typescript

// lib/analytics.ts
export function trackEvent(
  event: string,
  properties?: Record<string, any>
) {
  if (typeof window === 'undefined') return;
  
  // Send to your analytics service
  console.log('Event:', event, properties);
  
  // Example: Google Analytics
  if (window.gtag) {
    window.gtag('event', event, properties);
  }
}

// Usage
trackEvent('content_exported', {
  format: 'csv',
  itemCount: 150,
});

Common Production Issues

Issue 1: CORS Errors

Symptom: API calls fail with CORS errors

Solution: Ensure your deployment URL is added to allowed origins in API configuration.

Issue 2: Environment Variables Not Working

Symptom: process.env.NEXT_PUBLIC_* is undefined

Solution:

  • Rebuild after adding environment variables
  • Verify variables start with NEXT_PUBLIC_
  • Check they're added in hosting platform settings

Issue 3: Hydration Errors

Symptom: "Hydration failed" errors in console

Solution:

typescript

// Use useEffect for client-only rendering
const [mounted, setMounted] = useState(false);

useEffect(() => {
  setMounted(true);
}, []);

if (!mounted) return null;

Issue 4: Slow Initial Load

Symptom: App takes 5+ seconds to load

Solution:

  • Enable code splitting
  • Optimize bundle size
  • Use CDN for static assets
  • Implement proper caching headers

Publishing to Public Marketplace

Once the public Marketplace launches fully, you can publish your app.

Requirements for Public Apps

  1. Documentation
    • Clear README with setup instructions
    • Screenshots and demo video
    • Support contact information
  2. Quality Standards
    • No critical bugs
    • Responsive design
    • Proper error handling
    • Accessibility compliance
  3. Pricing Model
    • Free
    • Freemium (basic free, premium paid)
    • Paid subscription
    • One-time purchase

Publishing Process

  1. Test Thoroughly
    • Get beta users to test
    • Fix all reported issues
    • Document known limitations
  2. Create Marketing Materials
    • App icon (512x512px)
    • Screenshots (various sizes)
    • Demo video (2-3 minutes)
    • Feature highlights
    • Use case examples
  3. Submit for Review
    • In Developer Studio, change app type to "Public"
    • Complete listing information
    • Submit for Sitecore review
    • Wait for approval (typically 5-7 business days)
  4. Launch and Promote
    • Announce on social media
    • Write blog post
    • Submit to Sitecore community
    • Engage with early adopters

Final Checklist

Before considering your app production-ready:

Development

  • All features tested and working
  • No TypeScript errors
  • ESLint warnings resolved
  • Code reviewed and optimized

Performance

  • Bundle size under 1MB initial load
  • First Contentful Paint < 2s
  • Time to Interactive < 5s
  • Lighthouse score > 90

Security

  • No hardcoded secrets
  • Input validation implemented
  • CSP headers configured
  • Rate limiting in place

User Experience

  • Loading states for all async operations
  • Error messages are helpful
  • Responsive on all devices
  • Keyboard navigation works
  • Color contrast meets WCAG AA

Deployment

  • Deployed to production hosting
  • Custom domain configured (optional)
  • SSL certificate active
  • Environment variables set
  • App registration updated in Developer Studio

Monitoring

  • Error tracking configured
  • Analytics implemented
  • Performance monitoring active
  • Uptime monitoring setup

Conclusion

You've made it! You now have the complete knowledge to:

  1. Build sophisticated Marketplace apps from scratch
  2. Integrate with XM Cloud APIs and extension points
  3. Deploy to production hosting platforms
  4. Optimize for performance and security
  5. Monitor and maintain your apps

What's Next for You?

Immediate Actions:

  • Deploy your first app to production
  • Share it with your team
  • Gather user feedback
  • Iterate and improve

Long-term Goals:

  • Build apps for the public Marketplace
  • Create a portfolio of Sitecore extensions
  • Contribute to the Sitecore community
  • Potentially build a business around Marketplace apps

Resources to Bookmark


Thank You!

This three-part series covered everything from "What is Sitecore Marketplace?" to "How do I deploy a production app?"

If you found this helpful:

  • Share it with your developer community
  • Try building your own Marketplace app
  • Let me know what you build!

Questions? Feedback? Success stories? Drop them in the comments below. I read every single one.

Happy coding, and see you in the Marketplace! 🚀


Missed the earlier parts?