I’m hoping to get ideas/feedback for the best to structure a project I’m working on. It’s a feedback widget that needs to be embeddable in other sites via a script tag, and I want the main app/dashboard/etc. to be a Next.js app. I do not want to use React for the widget because that makes the script too large. I’d also like to share UI styling as much as possible to avoid duplication.
So far my thoughts are to use a Turborepo like this: app/web - Next.js app (landing pages/dashboard) app/widget - Vite + Lit web-components app (feedback widget) (possibly just vanilla js if its easier somehow) packages/ui - Tailwind-variants based framework-agnostic “components”, i.e. just the class string outputs for things like button/input/etc.
This way I have each app running independently so I can configure them as needed. I can deploy app/web to my domain example.com and the widget script can be deployed to something like scripts.example.com/embed.js. And I can share styles between the two even though they’re different frameworks.
Downsides are:
This seems pretty complex, maybe theres a simpler solution?
I’d like to be able to use some parts of the app/widget components directly inside my app/web app, like on the customization preview page or as an example on the landing page. But this would mean importing an app inside an app which the docs say not to do.
Some style re-use between apps but not real component sharing.
Thinking on it again I think I might need to move the Vite+Lit repo down into packages/lit-component (or something) that way they can be used directly in my app/web Next app in the few places I need them. Then app/widget just imports those components, wraps them in the embed script logic, and bundles them as an iife. I could maybe even create a packages/react-lit-compoents that wraps these lit components in @lit/react for easier integration into my Next app.
This prevents having to install app code in another app. But also now it’s getting more complex.
Your Turborepo setup is looking really solid. This pattern is actually quite common for projects like yours! That said, here are a few ideas to help you address some of the concerns you brought up:
1. Complexity:
Honestly, this feels like the right level of complexity for what you’re building. Having that separation gives you the flexibility to handle different deployment targets and optimize for bundle size which sounds like exactly what you need.
2. Sharing widget components in the Next.js app:
Rather than importing the entire widget app directly, you could try:
Creating a packages/widget-components package that exports your Lit components as a standalone library
Then both apps/widget and apps/web can import from that shared package
In the Next.js app, you can either use the Lit components directly (with SSR considerations in mind), or wrap them in React components for smoother integration
3. Component sharing overall:
Your existing packages/ui setup with Tailwind Variants is a great start! You could also consider adding:
packages/widget-components → your Lit-based components
packages/react-components → React wrappers around those Lit components (specifically for the Next.js app)
For deploying the widget script itself:
You can use Vercel’s static file hosting for the widget bundle
Add proper CORS headers via vercel.json
And if you ever need dynamic widget config, Vercel Edge Functions could come in handy too