Skip to main content

Documentation Index

Fetch the complete documentation index at: https://docs.tagada.io/llms.txt

Use this file to discover all available pages before exploring further.

Path Remapping Guide for React Plugins

Feature Overview

Path remapping allows you to customize external URLs while maintaining your plugin’s internal routing logic. Perfect for SEO-optimized URLs, branded paths, and multi-market operations.

Framework

React SDK v2 - Requires React with TanStack Query
React is Required: This guide covers path remapping for React-based TagadaPay plugins. All code examples use React hooks and patterns from the TagadaPay SDK v2.

What is Path Remapping?

Path remapping lets you expose custom, SEO-friendly URLs to your users while your plugin continues to use its original internal paths internally.

Example Scenario

Your plugin defines these internal paths:
/checkout
/checkout/step1
/checkout/step2
But you want customers to see:
/buy-now
/buy-now/shipping  
/buy-now/payment
Path remapping makes this possible!

SEO Benefits

Use keyword-rich URLs like /buy-now instead of /checkout

Brand Consistency

Match URLs to your brand language across markets

A/B Testing

Test different URL structures without changing plugin code

Multi-Market

Localized URLs: /acheter (FR) vs /kaufen (DE) vs /checkout (EN)

How It Works

1

You configure the mapping in CRM

Tell TagadaPay: “When users visit /custom/:id, route to plugin’s /hello-with-param/:myparam
2

TagadaPay injects configuration

The platform automatically injects the mapping as a meta tag in your plugin’s HTML
3

SDK handles routing

The SDK transparently handles URL matching and parameter extraction
4

Your plugin works unchanged

Your code continues to use internal paths - no modifications needed!

Architecture

Configuration

In Plugin Manifest

Mark which pages support remapping:
{
  "pluginId": "my-checkout-plugin",
  "pages": [
    {
      "path": "/",
      "remappable": false  // Static landing page
    },
    {
      "path": "/checkout",
      "remappable": true   // ✅ Can be remapped
    },
    {
      "path": "/checkout/:step",
      "remappable": true   // ✅ Can be remapped
    },
    {
      "path": "/thankyou/:orderId",
      "remappable": true   // ✅ Can be remapped
    }
  ]
}

In CRM (Done by Merchant)

Merchants configure remapping when mounting your plugin:
// CRM UI allows configuring:
{
  from: "/checkout",      // Your plugin's original path
  to: "/buy-now",         // Custom external URL
  matcher: "^/buy-now.*"  // Route matcher pattern
}

SDK Usage

Basic Routing with shouldMatchRoute()

import { shouldMatchRoute } from '@tagadapay/plugin-sdk/v2';
import { Route, Routes } from 'react-router-dom';

function App() {
  return (
    <Routes>
      <Route path="/*" element={<RemappableRoutes />} />
    </Routes>
  );
}

function RemappableRoutes() {
  // SDK automatically handles remapping!
  if (shouldMatchRoute('/checkout')) {
    return <CheckoutPage />;
  }
  
  if (shouldMatchRoute('/thankyou/:orderId')) {
    return <ThankYouPage />;
  }
  
  return <NotFound />;
}
What happens:
  • User visits: /buy-now (external URL)
  • SDK knows: “This is actually /checkout
  • shouldMatchRoute('/checkout') returns true
  • Your component renders correctly!

Parameter Extraction with matchRoute()

New in v2.7.14: matchRoute() extracts URL parameters from remapped paths automatically!
import { matchRoute } from '@tagadapay/plugin-sdk/v2';

function RemappableRoutes() {
  const location = useLocation();
  
  // Check and extract params in one call
  const thankYouMatch = matchRoute('/thankyou/:orderId');
  
  if (thankYouMatch.matched) {
    // ✅ Params are automatically extracted!
    return <ThankYouPage orderId={thankYouMatch.params.orderId} />;
  }
  
  const checkoutMatch = matchRoute('/checkout/:step');
  
  if (checkoutMatch.matched) {
    return <CheckoutPage step={checkoutMatch.params.step} />;
  }
  
  return <NotFound />;
}
Example:
External URL: /order-confirmation/ORD-12345
Internal path: /thankyou/:orderId
Result: { matched: true, params: { orderId: 'ORD-12345' } }

Getting Path Information with getPathInfo()

import { getPathInfo } from '@tagadapay/plugin-sdk/v2';

function PathDebugger() {
  const pathInfo = getPathInfo();

  return (
    <div>
      <h2>Path Information</h2>
      <dl>
        <dt>External URL (what user sees):</dt>
        <dd>{pathInfo.externalPath}</dd>
        
        <dt>Internal Path (plugin's perspective):</dt>
        <dd>{pathInfo.internalPath || 'No remapping'}</dd>
        
        <dt>Is Remapped:</dt>
        <dd>{pathInfo.isRemapped ? 'Yes' : 'No'}</dd>
        
        <dt>Query Parameters:</dt>
        <dd>{pathInfo.query.toString()}</dd>
      </dl>
    </div>
  );
}

Real-World Examples

Example 1: E-commerce Checkout

Plugin defines:
/checkout           → Main checkout page
/checkout/shipping  → Shipping step
/checkout/payment   → Payment step
/checkout/confirm   → Review step
Merchant wants:
/secure-checkout           → Main checkout page
/secure-checkout/delivery  → Shipping step
/secure-checkout/pay       → Payment step
/secure-checkout/review    → Review step
Your plugin code (unchanged!):
function CheckoutRoutes() {
  const shippingMatch = matchRoute('/checkout/shipping');
  const paymentMatch = matchRoute('/checkout/payment');
  const confirmMatch = matchRoute('/checkout/confirm');
  
  if (shippingMatch.matched) return <ShippingPage />;
  if (paymentMatch.matched) return <PaymentPage />;
  if (confirmMatch.matched) return <ConfirmPage />;
  
  return <CheckoutLandingPage />;
}
Result: Works perfectly with both URL structures! ✅

Example 2: Thank You Page with Order ID

Plugin expects:
/thankyou/:orderId
Merchant wants:
/order-complete/:orderId
Your plugin code:
function ThankYouRoutes() {
  const match = matchRoute('/thankyou/:orderId');
  
  if (match.matched) {
    // ✅ Works with both /thankyou/123 and /order-complete/123
    return <ThankYouPage orderId={match.params.orderId} />;
  }
  
  return <NotFound />;
}

Example 3: Multi-Market Localization

Plugin internal paths:
/checkout → Universal checkout logic
Different markets:
🇺🇸 English: /checkout
🇫🇷 French:  /paiement
🇩🇪 German:  /kasse
🇪🇸 Spanish: /comprar
Your plugin:
// Same code works for all markets!
function App() {
  if (shouldMatchRoute('/checkout')) {
    return <CheckoutPage />;
  }
}
Each market configures their preferred external URL in the CRM. Your plugin works everywhere! 🌍

Example 4: Product Pages with Slugs

Plugin:
/product/:productId
SEO-Friendly URLs:
/p/amazing-wireless-headphones
/p/ultra-comfy-sneakers
/p/premium-coffee-beans
Implementation:
function ProductRoutes() {
  const match = matchRoute('/product/:productId');
  
  if (match.matched) {
    // ✅ productId = "amazing-wireless-headphones"
    return <ProductPage slug={match.params.productId} />;
  }
  
  return <ProductGrid />;
}

Parameter Name Rules

Important: When remapping paths with parameters, parameter names must match between external and internal paths.

✅ Valid Remapping

{
  "from": "/hello-with-param/:myparam",
  "to": "/custom/:myparam"
}
Both use :myparamWorks!

❌ Invalid Remapping

{
  "from": "/hello-with-param/:myparam",
  "to": "/custom/:id"  
}
Parameter names don’t match → CRM will reject this Why? This ensures reliable parameter extraction without complex name mapping logic.

Development & Testing

Local Development

Test path remapping in your local environment using localStorage:
// Open browser console and run:
localStorage.setItem('tagadapay-remap', JSON.stringify({
  externalPath: '/custom/:myparam',
  internalPath: '/hello-with-param/:myparam'
}));

// Then visit: http://localhost:5173/custom/test123
// Your plugin receives it as: /hello-with-param/test123
Clear remapping:
localStorage.removeItem('tagadapay-remap');

Query Parameter Method

Alternative testing method:
http://localhost:5173/hello-with-param/123?__remap=/my-custom-path/123

Debugging

Add a debug component to see what’s happening:
import { getPathInfo, matchRoute } from '@tagadapay/plugin-sdk/v2';

function PathRemapDebugger() {
  const pathInfo = getPathInfo();
  const match = matchRoute('/your-internal-path/:param');
  
  return (
    <div className="debug-panel">
      <h3>🔍 Path Remap Debug Info</h3>
      
      <div>
        <strong>Current URL:</strong> {window.location.pathname}
      </div>
      
      <div>
        <strong>Is Remapped:</strong> {pathInfo.isRemapped ? '✅ Yes' : '❌ No'}
      </div>
      
      {pathInfo.isRemapped && (
        <>
          <div>
            <strong>External Path:</strong> {pathInfo.externalPath}
          </div>
          <div>
            <strong>Internal Path:</strong> {pathInfo.internalPath}
          </div>
        </>
      )}
      
      <div>
        <strong>Route Match:</strong> {match.matched ? '✅ Matched' : '❌ No match'}
      </div>
      
      {match.matched && match.params && (
        <div>
          <strong>Extracted Params:</strong>
          <pre>{JSON.stringify(match.params, null, 2)}</pre>
        </div>
      )}
    </div>
  );
}

Production Behavior

Automatic Meta Tag Injection

In production, TagadaPay automatically injects remapping configuration:
<head>
  <meta name="tagadapay-path-remap" 
        content="%7B%22external%22%3A%22%2Fcustom%2F%3Amyparam%22%2C%22internal%22%3A%22%2Fhello-with-param%2F%3Amyparam%22%7D">
</head>
Decoded:
{
  "external": "/custom/:myparam",
  "internal": "/hello-with-param/:myparam"
}
The SDK reads this automatically - no action needed from you!

CDN Distribution

Remapped plugins are served from CDN subdomains:
Original:  https://store-123.cdn.tagadapay.com/checkout
Remapped:  https://store-123.cdn.tagadapay.com/buy-now

(Both serve the same plugin, SDK handles the routing)

Best Practices

1. Always Use SDK Functions

Don’t parse window.location manually! Always use SDK functions for path matching.
❌ Don’t do this:
// This won't work with remapping!
function CheckoutPage() {
  const isCheckout = window.location.pathname === '/checkout';
  // ...
}
✅ Do this:
// This works with remapping!
function CheckoutPage() {
  const isCheckout = shouldMatchRoute('/checkout');
  // ...
}

2. Mark Pages as Remappable

In your plugin.manifest.json:
{
  "pages": [
    {
      "path": "/admin-panel",
      "remappable": false  // Internal tool, no need for remapping
    },
    {
      "path": "/checkout",
      "remappable": true   // Customer-facing, allow remapping
    }
  ]
}

3. Keep Internal Paths Descriptive

Even if merchants remap them, keep your internal paths clear: Good:
/checkout
/checkout/shipping
/product/:productId
/thankyou/:orderId
Avoid:
/c
/s1
/p/:id
/ty/:oid

4. Handle Both Scenarios

Your plugin should work whether remapping is active or not:
function App() {
  // Works with:
  // - /checkout (no remapping)
  // - /buy-now (remapped to /checkout)
  // - /secure-payment (remapped to /checkout)
  
  if (shouldMatchRoute('/checkout')) {
    return <CheckoutPage />;
  }
  
  return <HomePage />;
}

5. Test Both Paths

During development, test your plugin with:
  1. Original paths: http://localhost:5173/checkout
  2. Remapped paths: Use localStorage to simulate remapping

API Reference

shouldMatchRoute(internalPath: string): boolean

Check if the current URL matches the given internal path. Parameters:
  • internalPath: Your plugin’s internal path pattern (e.g., /checkout, /product/:id)
Returns:
  • boolean: true if current URL matches (considering remapping), false otherwise
Example:
if (shouldMatchRoute('/checkout/:step')) {
  return <CheckoutPage />;
}

matchRoute(internalPath: string): RouteMatchResult

Check if route matches AND extract URL parameters. Parameters:
  • internalPath: Your plugin’s internal path pattern
Returns:
{
  matched: boolean;
  params: Record<string, string>;
}
Example:
const result = matchRoute('/product/:productId');
// result.matched = true
// result.params = { productId: 'wireless-headphones' }

if (result.matched) {
  return <ProductPage productId={result.params.productId} />;
}

getPathInfo(): PathInfo

Get detailed information about the current path and remapping status. Returns:
{
  externalPath: string;        // URL user sees
  internalPath: string | null; // Your plugin's path (if remapped)
  isRemapped: boolean;          // Whether remapping is active
  query: URLSearchParams;       // Query parameters
  hash: string;                 // URL hash
}
Example:
const info = getPathInfo();

if (info.isRemapped) {
  console.log(`User sees: ${info.externalPath}`);
  console.log(`Plugin receives: ${info.internalPath}`);
}

getInternalPath(): string | null

Get the current internal path (or null if no remapping). Returns:
  • string: Internal path if remapping is active
  • null: If no remapping is active
Example:
const internalPath = getInternalPath();

if (internalPath) {
  console.log('Remapping active! Internal path:', internalPath);
} else {
  console.log('No remapping - using original paths');
}

Troubleshooting

Parameters are empty

Problem: matchRoute() returns { matched: true, params: {} } Solution: Ensure parameter names match in your manifest and CRM configuration:
// ✅ Correct
{
  "from": "/product/:productId",
  "to": "/p/:productId"
}

// ❌ Wrong
{
  "from": "/product/:productId",
  "to": "/p/:id"  // Different parameter name!
}

Route not matching

Problem: shouldMatchRoute() returns false when it should match Checklist:
  1. Is the page marked as remappable: true in your manifest?
  2. Is the internal path correct (check with getPathInfo())?
  3. Are you using the SDK function (not window.location)?
  4. Try adding logging: console.log(getPathInfo())

Works locally but not in production

Problem: Remapping works in development but fails on CDN Solution:
  1. Check that meta tag is injected: View page source and search for tagadapay-path-remap
  2. Rebuild your plugin: pnpm build
  3. Redeploy: npx @tagadapay/plugin-cli deploy
  4. Clear browser cache and CDN cache

Remapping not working locally

Problem: localStorage remapping doesn’t work Solution:
// 1. Clear any existing config
localStorage.clear();

// 2. Set new config (ensure it's valid JSON)
localStorage.setItem('tagadapay-remap', JSON.stringify({
  externalPath: '/your-external/:param',
  internalPath: '/your-internal/:param'
}));

// 3. Reload the page (hard refresh: Cmd+Shift+R or Ctrl+Shift+R)

Migration Guide

From Manual Routing

Before (manual routing):
function App() {
  const location = useLocation();
  
  // ❌ Doesn't work with remapping
  if (location.pathname === '/checkout') {
    return <CheckoutPage />;
  }
}
After (SDK routing):
import { shouldMatchRoute } from '@tagadapay/plugin-sdk/v2';

function App() {
  // ✅ Works with remapping!
  if (shouldMatchRoute('/checkout')) {
    return <CheckoutPage />;
  }
}

From React Router Params

Before:
import { useParams } from 'react-router-dom';

function ProductPage() {
  const { productId } = useParams(); // ❌ Might be empty with remapping
  return <Product id={productId} />;
}
After:
import { matchRoute } from '@tagadapay/plugin-sdk/v2';

function ProductRoutes() {
  const match = matchRoute('/product/:productId');
  
  if (match.matched) {
    // ✅ Always works, even with remapping
    return <ProductPage id={match.params.productId} />;
  }
  
  return <NotFound />;
}

Advanced Patterns

Nested Routes with Remapping

function App() {
  const checkoutMatch = matchRoute('/checkout/:step');
  
  if (checkoutMatch.matched) {
    return <CheckoutFlow step={checkoutMatch.params.step} />;
  }
  
  return <HomePage />;
}

function CheckoutFlow({ step }: { step: string }) {
  switch (step) {
    case 'shipping':
      return <ShippingStep />;
    case 'payment':
      return <PaymentStep />;
    case 'review':
      return <ReviewStep />;
    default:
      return <CheckoutLanding />;
  }
}

Conditional Remapping Display

function PathBreadcrumbs() {
  const pathInfo = getPathInfo();
  
  if (pathInfo.isRemapped) {
    return (
      <nav>
        <span>You are here: {pathInfo.externalPath}</span>
        <small>(Internal: {pathInfo.internalPath})</small>
      </nav>
    );
  }
  
  return <nav>{window.location.pathname}</nav>;
}

SEO Meta Tags with Remapping

import { Helmet } from 'react-helmet';

function CheckoutPage() {
  const pathInfo = getPathInfo();
  
  // Use external path for canonical URL (what user/Google sees)
  const canonicalUrl = `https://example.com${pathInfo.externalPath}`;
  
  return (
    <>
      <Helmet>
        <link rel="canonical" href={canonicalUrl} />
        <meta property="og:url" content={canonicalUrl} />
      </Helmet>
      <div>Checkout content...</div>
    </>
  );
}

Next Steps

API Reference

Complete SDK API documentation

Examples

Real-world plugin examples

Best Practices

Optimization tips and patterns

Tutorial

Build your first plugin

Need Help?