Skip to content

feat(ENG-11821): add SSR support#146

Merged
adefreitas merged 2 commits intoStackOneHQ:mainfrom
adefreitas:ENG-11821/add-ssr-support
Apr 29, 2026
Merged

feat(ENG-11821): add SSR support#146
adefreitas merged 2 commits intoStackOneHQ:mainfrom
adefreitas:ENG-11821/add-ssr-support

Conversation

@adefreitas
Copy link
Copy Markdown
Collaborator

@adefreitas adefreitas commented Apr 29, 2026

Summary

  • Make @stackone/hub safe to import from server-rendered apps (Next.js App Router, Remix, etc.) by adding a 'use client' banner to the bundle and guarding render-time DOM access.
  • Spin up a dedicated Next.js sandbox at dev/nextjs/ and restructure the existing Vite sandbox into dev/vite/ so both consume the hub via file:../.. — same shape as a real consumer.
  • Document the SSR contract and the duplicate-React caveat in the README, plus a new CLAUDE.md for repo conventions.

What changed

Bundle / runtime

  • 'use client' is injected as a rollup output.banner and survives terser via compress.directives: false.
  • useIntegrationPicker now guards window.location.origin access (was thrown during render under SSR).
  • WebComponentWrapper guards customElements.define with typeof window/customElements checks plus a customElements.get registration check.
  • package.json now declares sideEffects: ["./dist/webcomponent.js"] so consumers tree-shake the React entry properly.

Dev sandboxes

  • dev/nextjs/ — Next 15 + React 19 App Router. Validates SSR end-to-end. Runs on :3002.
  • dev/vite/ — the existing Vite playground, now its own package. Runs on :3001. resolve.dedupe for react/react-dom/react-hook-form is required because the symlinked hub at node_modules/@stackone/hub resolves into a directory with its own node_modules (would otherwise hit "Invalid hook call").
  • Root scripts: npm run dev, dev:setup, dev:nextjs, dev:nextjs:setup, relay all delegate into the right sandbox.

Docs

  • README: Next.js (App Router) usage, suppressHydrationWarning on <html> (the malachite theme writes CSS variables to documentElement post-hydration), and a troubleshooting section for the duplicate-React issue covering Vite / webpack/Next / pnpm.
  • New CLAUDE.md documenting commands, project layout, SSR invariants, and the single-React-instance constraint.

Test plan

  • npm run lint — clean (55 files, no fixes)
  • npm run build — emits ESM, CJS, IIFE, .d.ts; ESM/CJS bundles begin with "use client";
  • npm run dev:nextjs:setup && npm run dev:nextjs — Next sandbox boots on :3002, / returns HTTP 200, page is statically pre-rendered
  • npm run dev:setup && npm run dev — Vite sandbox boots on :3001; served bundle resolves all React imports to a single Vite pre-bundle (verified via served module graph)
  • Smoke test the integration picker in both sandboxes against a live token
  • Confirm hydration is clean in the Next sandbox after pasting a token

🤖 Generated with Claude Code


Summary by cubic

Add SSR support to @stackone/hub so it can be imported in Next.js App Router and other SSR frameworks without render-time errors. Includes a Next.js sandbox and docs to guide adoption, addressing ENG-11821.

  • New Features

    • Bundles now start with 'use client' via Rollup banners; Terser preserves the directive.
    • SSR guards added: no render-time window access in useIntegrationPicker; WebComponentWrapper checks window/customElements and avoids double registration; package.json sets sideEffects: ["./dist/webcomponent.js"] for proper tree-shaking.
    • Dev sandboxes: new Next.js 15 + React 19 App Router app in dev/nextjs; Vite sandbox moved to dev/vite with resolve.dedupe for react, react-dom, react-hook-form. Root scripts delegate to each sandbox.
    • Docs & DX: fixed web component docs to use dist/webcomponent.js and <stackone-hub>; committed .env.example files and adjusted .gitignore; Next.js sandbox refuses to send STACKONE_API_KEY to non-HTTPS/non-localhost API URLs; theme toggle now has an accessible label; CLAUDE.md path corrections.
  • Migration

    • Next.js App Router: add suppressHydrationWarning to the <html> tag in your root layout to avoid theme-related hydration warnings.
    • Ensure a single React instance: dedupe or alias react/react-dom (e.g., Vite resolve.dedupe, Next/Webpack aliases). For pnpm, use public-hoist-pattern[]=react* or shamefully-hoist=true.

Written for commit 3e38418. Summary will update on new commits. Review in cubic

Make @stackone/hub safe to consume from server-rendered apps and
restructure the dev sandboxes to match real-consumer usage.

Bundle / runtime
- Inject 'use client' banner into the ESM and CJS outputs (rollup
  output.banner) and configure terser with compress.directives: false
  so the directive survives minification.
- Guard window.location.origin access in useIntegrationPicker so the
  hook is render-safe under SSR.
- Guard customElements.define in WebComponentWrapper with typeof
  window/customElements checks plus a customElements.get registration
  check.
- Add a sideEffects field marking only dist/webcomponent.js, so
  bundlers can tree-shake the React entry.

Dev sandboxes
- New dev/nextjs/ — Next.js 15 + React 19 App Router app that consumes
  the hub via "@stackone/hub": "file:../..", to validate SSR end-to-end
  (port 3002).
- Move the existing Vite sandbox into dev/vite/ as its own package,
  also linking the hub via file:../.. (port 3001). resolve.dedupe is
  set for react/react-dom/react-hook-form so the symlinked hub doesn't
  end up with a second React instance.
- Root scripts: npm run dev / dev:setup / dev:nextjs / dev:nextjs:setup
  / relay all delegate into the appropriate sandbox. Relay/Vite
  devDeps moved with the sandbox.

Docs
- README: Next.js (App Router) usage, suppressHydrationWarning note
  for theme application, and a troubleshooting section on the
  duplicate-React error covering Vite, webpack/Next, and pnpm fixes.
- New CLAUDE.md capturing SSR invariants, sandbox conventions, and
  the single-React-instance constraint.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Copilot AI review requested due to automatic review settings April 29, 2026 08:32
Copy link
Copy Markdown

@claude claude Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Claude Code Review

This pull request is from a fork — automated review is disabled. A repository maintainer can comment @claude review to run a one-time review.

Copy link
Copy Markdown

@cubic-dev-ai cubic-dev-ai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

2 issues found across 34 files

Prompt for AI agents (unresolved issues)

Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.


<file name="dev/nextjs/app/page.tsx">

<violation number="1" location="dev/nextjs/app/page.tsx:5">
P1: Custom agent: **Flag Security Vulnerabilities**

Sensitive API key is sent to an unvalidated env-configured endpoint, allowing potential credential exfiltration or insecure HTTP transport.</violation>
</file>

<file name="CLAUDE.md">

<violation number="1" location="CLAUDE.md:27">
P3: The project layout entry incorrectly points the Vite sandbox to `dev/`; it should reference `dev/vite/` to match the actual structure and later instructions in the same file.</violation>
</file>

Reply with feedback, questions, or to request a fix. Tag @cubic-dev-ai to re-run a review.

Comment thread dev/nextjs/app/page.tsx
Comment thread CLAUDE.md Outdated
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Adds SSR-safe importing for @stackone/hub (Next.js/Remix/etc.) and introduces dedicated Vite + Next.js sandboxes that consume the package via file:../.., alongside updated documentation for SSR and React deduping.

Changes:

  • Injects a 'use client' banner into ESM/CJS bundles and configures terser to preserve directives.
  • Guards render-time DOM access (window.location.origin, customElements.define) to prevent SSR crashes.
  • Restructures dev sandboxes into dev/vite/ and adds dev/nextjs/ for SSR validation; updates scripts/docs accordingly.

Reviewed changes

Copilot reviewed 22 out of 34 changed files in this pull request and generated 3 comments.

Show a summary per file
File Description
vite.config.ts Removes the old root Vite config (sandbox moved under dev/vite/).
src/modules/integration-picker/hooks/useIntegrationPicker.ts Guards window.location.origin access for SSR safety.
src/WebComponentWrapper.tsx Adds SSR-safe customElements registration guard + double-registration check.
src/StackOneHub.tsx Adds 'use client' directive to the component module.
rollup.config.mjs Adds 'use client' banners to ESM/CJS and preserves directives through terser.
package.json Adds sideEffects for the webcomponent bundle; delegates dev scripts into sandboxes.
package-lock.json Updates lockfile to reflect dependency/script re-org.
dev/vite/vite.config.ts New Vite sandbox config with resolve.dedupe to avoid duplicate React instances.
dev/vite/* New Vite sandbox package + relay config/schema and generated artifacts.
dev/nextjs/* New Next.js App Router sandbox package to validate SSR behavior.
biome.json Ignores __generated__ and .next output directories.
README.md Documents SSR contract, sandboxes, Next.js usage, and duplicate-React troubleshooting.
CLAUDE.md Adds repo conventions, commands, and SSR invariants documentation.
Files not reviewed (1)
  • dev/nextjs/package-lock.json: Language not supported

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread README.md
Comment thread README.md
Comment thread dev/nextjs/app/HubWrapper.tsx Outdated
- .gitignore: stop ignoring .env.example so the dev sandboxes' example
  files actually get committed (the README referenced one that wasn't
  in the repo).
- Add the missing dev/nextjs/.env.example and dev/vite/.env.example.
- CLAUDE.md: project layout pointed Vite sandbox at dev/ instead of
  dev/vite/.
- README: web component snippet was still using the old StackOneHub.web.js
  / <my-component> names — switched to webcomponent.js / <stackone-hub>
  to match what rollup actually emits.
- dev/nextjs/app/HubWrapper.tsx: theme toggle had no accessible label.
- dev/nextjs/app/page.tsx: refuse to send STACKONE_API_KEY to a
  NEXT_PUBLIC_API_URL that isn't HTTPS (or localhost) so a misconfigured
  env can't leak the key over plaintext.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Copy link
Copy Markdown

@cubic-dev-ai cubic-dev-ai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

1 issue found across 7 files (changes from recent commits).

Prompt for AI agents (unresolved issues)

Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.


<file name="dev/nextjs/.env.example">

<violation number="1" location="dev/nextjs/.env.example:6">
P2: Use the Next.js sandbox port (3002) for `NEXT_PUBLIC_APP_URL` to avoid pointing local config at the wrong origin.</violation>
</file>

Reply with feedback, questions, or to request a fix. Tag @cubic-dev-ai to re-run a review.

Comment thread dev/nextjs/.env.example
@adefreitas adefreitas merged commit 29411f9 into StackOneHQ:main Apr 29, 2026
5 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants