CA

What does this PR do?

This PR tackles the performance caused by the App Store / circular dependencies. /claim #23104

The Diagnosis: Using madge to analyze the dependency graph, I identified a critical circular dependency chain (75+ cycles) where the generic InstallAppButton was statically importing the entire App Store catalog (apps.browser.generated.tsx).

The chain looked like this: InstallAppButton -> InstallAppButtonWithoutPlanCheck -> InstallAppButtonMap (The whole store) -> Alby (and others) -> AppCard -> OmniInstallAppButton -> InstallAppButton.

The Fix: I refactored InstallAppButtonWithoutPlanCheck.tsx to switch from a synchronous static import to an asynchronous dynamic import using useEffect.

  • This breaks the circular dependency chain at the source.
  • The InstallAppButtonMap is now lazy-loaded only when the component mounts, preventing the entire App Store from being bundled into the initial page load of the Dashboard/Settings.

Note: Due to hardware limitations (my machine cannot handle the 16GB RAM requirement for the Node process in this monorepo) and issues verifying via Codespaces, I was unable to run the full dev server or the test suite locally.

I coded this fix based on static analysis data and assistance from LLMs (Gemini) to ensure the React Hooks logic (useState, useEffect) handles the async loading safely (including isMounted checks). I am requesting the maintainers or reviewers to please verify the runtime behavior and test suite on my behalf.

  • Fixes #23104

Visual Demo (For contributors especially)

Before the fix (Analysis Log): Running npx madge --circular --extensions ts,tsx packages/app-store resulted in 75 circular dependencies. The root cause was the generic install button pulling in every app:

75) InstallAppButton.tsx > InstallAppButtonWithoutPlanCheck.tsx > apps.browser.generated.tsx > alby/components/EventTypeAppCardInterface.tsx > _components/AppCard.tsx > _components/OmniInstallAppButton.tsx

After the fix: The specific cycle involving InstallAppButton and apps.browser.generated.tsx is broken. The madge count should drop significantly (ideally to 1 or 0 runtime cycles), and the initial JS bundle size for the Dashboard should be drastically reduced.

Mandatory Tasks (DO NOT REMOVE)

  • I have self-reviewed the code (A decent size PR without self-review might be rejected).
  • N/A (No documentation changes required).
  • I confirm automated tests are in place that prove my fix is effective or that my feature works. (Unchecked: As noted above, I could not run the test suite locally due to hardware constraints. I rely on CI/CD and reviewer assistance here.)

How should this be tested?

Since I could not run the environment locally, please test the following steps:

  1. Architecture Verification: Run npx madge --circular --extensions ts,tsx packages/app-store. Confirm that the circular dependency count has dropped and the InstallAppButton loop is gone.
  2. UI Verification: Navigate to the App Store or any page using the InstallAppButton.
    • Ensure the button still renders correctly.
    • Click “Install” on a generic app (e.g., Weather) and a custom app (e.g., Stripe) to ensure the modal/action still triggers after the async load.
  3. Performance: Observe the .next/server/chunks folder or the network tab. The initial bundle size should be smaller.
  4. Tests: Please run yarn workspace @calcom/app-store test.
    • Note: Since the component is now async, tests using getByRole might need to be updated to await findByRole to handle the useEffect timing.

Checklist

  • I have read the contributing guide
  • My code follows the style guidelines of this project
  • I have commented my code, particularly in hard-to-understand areas
  • I have checked if my changes generate no new warnings (Verified via static analysis)

Claim

Total prize pool $2,000
Total paid $0
Status Pending
Submitted November 28, 2025
Last updated November 28, 2025

Contributors

AL

Alexandros Pappas

@apappas1129

100%

Sponsors

CA

Cal.com, Inc.

@cal

$2,000