Implementing Secure Client-side Components in a Multi-tenant React Application

Implementing Secure Client-side Components in a Multi-tenant React Application

Background

CM64 Startup Studio is a platform that enables rapid MVP development through a multi-tenant architecture where users can create and deploy web applications using JSON configurations and custom components. The platform runs on Next.js 14 with the App Router, supporting multiple domains and applications from a single codebase.

The Challenge

Our main challenge was implementing secure user-defined components while maintaining a great developer experience. We needed to:

  1. Allow users to write components using familiar JSX syntax
  2. Ensure components couldn't affect other tenants
  3. Provide access to framework utilities without compromising security
  4. Handle runtime component loading in a Next.js server environment

Solution

We developed a secure component sandbox that transforms JSX at runtime while providing controlled access to React hooks and framework utilities. Here's a simplified version of our approach:


const SecureClientComponent = ({ componentCode, componentId, jcontext, props }) => {
// Transform JSX to JavaScript
  const transformedCode = transform(componentCode, {
    presets: ['react']
  }).code;

// Create isolated component context
  const framework = {
    React,
    useState,
    useEffect,
// ... other allowed utilities
  };

// Evaluate component in controlled environment
  const UserComponent = new Function('React', 'framework', `
    with (framework) {
      return ${transformedCode};
    }
  `)(React, framework);

  return (
    <ErrorBoundary>
      <UserComponent {...props} framework={framework} />
    </ErrorBoundary>
  );
};

Key Insights

  1. Using new Function() instead of eval() provides better scope control and security
  2. Runtime JSX transformation enables a familiar developer experience without build steps
  3. The with statement, while generally discouraged, proves useful for providing controlled access to framework utilities

Practical Takeaways

This implementation demonstrates how to:

  • Create secure sandboxes for user-defined code
  • Balance security with developer experience
  • Handle dynamic component loading in Next.js
  • Implement proper error boundaries for runtime code

Next Steps

We're planning to:

  • Implement component caching to improve performance
  • Add development tools for component preview and debugging
  • Create a validation system for component code
  • Add TypeScript support for better developer experience

Lessons for the Community

This approach shows how to implement secure, user-defined components in a multi-tenant environment while maintaining a good developer experience. The solution is applicable to any platform that needs to run user-provided React components safely.

Get In Touch 🤗

We would love to hear from you.