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:
- Allow users to write components using familiar JSX syntax
- Ensure components couldn't affect other tenants
- Provide access to framework utilities without compromising security
- 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
- Using
new Function()
instead ofeval()
provides better scope control and security - Runtime JSX transformation enables a familiar developer experience without build steps
- 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.