Skip to content

P-1953 Add Formo Analytics SDK examples across multiple frameworks#1

Merged
yosriady merged 17 commits intomainfrom
claude/refactor-examples-monorepo-VrmAI
Feb 25, 2026
Merged

P-1953 Add Formo Analytics SDK examples across multiple frameworks#1
yosriady merged 17 commits intomainfrom
claude/refactor-examples-monorepo-VrmAI

Conversation

@yosriady
Copy link
Contributor

@yosriady yosriady commented Feb 23, 2026

TODOs

  • test each example
    • dynamic
    • metamask
    • farcaster
    • page router
    • app router
    • porto
    • privy
    • reown
    • react
    • all ok except solana and react native requires a new SDK 1.28.0 published
  • archive old examples
  • update docs.formo.so to link to new example

Summary

This PR adds comprehensive example applications demonstrating Formo Analytics SDK integration across multiple web3 and mobile frameworks. The examples showcase how to integrate Formo's analytics capabilities with various wallet solutions and development platforms.

Key Changes

New Example Applications

  • with-next-app-router: Next.js App Router example with Scaffold-ETH integration, featuring contract debugging, block explorer, and wallet connections
  • with-next-page-router: Next.js Pages Router example with SIWE authentication and analytics tracking
  • with-privy: Privy embedded wallet integration example showing wallet management and transaction tracking
  • with-porto: Porto wallet integration example with web3 analytics
  • with-dynamic: Dynamic.xyz wallet integration example demonstrating multi-wallet support
  • with-farcaster: Farcaster Frame SDK integration example for mini-app analytics
  • with-react-native: React Native example with Expo, featuring wallet connections, event tracking, and settings management

Core Features Demonstrated

  • Wallet Integration: Examples with RainbowKit, Privy, Dynamic, Porto, and Farcaster wallet solutions
  • Event Tracking: Custom event tracking, screen navigation analytics, and semantic event logging
  • Web3 Operations: Transaction signing, message signing, balance checking, and chain switching
  • Analytics Configuration: Proper SDK initialization with write keys and environment-specific setup
  • UI Components: Reusable components for wallet connection, balance display, and transaction forms

Infrastructure & Configuration

  • Tailwind CSS styling across all examples
  • TypeScript configuration for type safety
  • Environment variable examples for API keys and configuration
  • Package.json configurations with appropriate dependencies
  • Build and development scripts for each framework

Documentation

  • README files for each example explaining features, setup, and usage
  • Configuration examples showing how to initialize Formo SDK
  • Comments and inline documentation for key integration points

Notable Implementation Details

  • All examples follow framework-specific best practices (App Router vs Pages Router, React Native patterns)
  • Consistent use of wagmi for web3 interactions across web examples
  • Proper error handling and loading states in UI components
  • Analytics events tracked at key user interactions (wallet connection, transactions, navigation)
  • Support for multiple chains and network switching where applicable

https://claude.ai/code/session_01Vth1WkLFrTqCTdjwZqFwby


Open with Devin

Note

Low Risk
Primarily adds CI and example/docs files; low risk aside from potential CI failures from dependency/build matrix and env-file templating.

Overview
Adds a GitHub Actions workflow (.github/workflows/build.yml) to build all example apps in CI via a matrix over directories, Node versions, and package managers, including basic .env generation from .env.example and a special-case nested env for with-next-app-router.

Bootstraps repo-level metadata (.gitignore, LICENSE.md) and replaces the placeholder root README.md with a catalog of the available examples.

Introduces a new with-dynamic Vite/React example scaffold, including its env template, local ignores, and extensive README describing Formo + Dynamic.xyz integration (plus basic HTML entrypoint).

Written by Cursor Bugbot for commit 784d7d8. This will update automatically on new commits. Configure here.

Migrated 10 separate Formo SDK example repositories into a single
monorepo following the resend/resend-examples flat collection pattern.

Examples included:
- with-react: React (CRA) with Formo Analytics
- with-next-app-router: Next.js App Router (Scaffold-ETH 2)
- with-next-page-router: Next.js Pages Router with RainbowKit
- with-privy: Next.js with Privy embedded wallets
- with-dynamic: React (Vite) with Dynamic.xyz wallet
- with-reown: Next.js with Reown AppKit
- with-porto: Next.js with Porto wallet
- with-solana: Next.js with Solana wallet adapter
- with-farcaster: Vite with Farcaster Mini App
- with-react-native: React Native (Expo)

Changes:
- Added root .gitignore, README.md, LICENSE.md
- Removed per-repo CI/CD workflows, git hooks, and contributing guides
- Fixed local dependency references (file:../sdk, link:../sdk-react-native)
  to use published npm packages

https://claude.ai/code/session_01Vth1WkLFrTqCTdjwZqFwby
@socket-security
Copy link

socket-security bot commented Feb 23, 2026

Warning

Review the following alerts detected in dependencies.

According to your organization's Security Policy, it is recommended to resolve "Warn" alerts. Learn more about Socket for GitHub.

Action Severity Alert  (click "▶" to expand/collapse)
Warn Critical
Critical CVE: Babel vulnerable to arbitrary code execution when compiling specifically crafted malicious code in npm @babel/traverse

CVE: GHSA-67hx-6x53-jw92 Babel vulnerable to arbitrary code execution when compiling specifically crafted malicious code (CRITICAL)

Affected versions: < 7.23.2; >= 8.0.0-alpha.0 < 8.0.0-alpha.4

Patched version: 7.23.2

From: ?npm/@trivago/prettier-plugin-sort-imports@4.1.1npm/@babel/traverse@7.17.3

ℹ Read more on: This package | This alert | What is a critical CVE?

Next steps: Take a moment to review the security alert above. Review the linked package source code to understand the potential risk. Ensure the package is not malicious before proceeding. If you're unsure how to proceed, reach out to your security team or ask the Socket team for help at support@socket.dev.

Suggestion: Remove or replace dependencies that include known critical CVEs. Consumers can use dependency overrides or npm audit fix --force to remove vulnerable dependencies.

Mark the package as acceptable risk. To ignore this alert only in this pull request, reply with the comment @SocketSecurity ignore npm/@babel/traverse@7.17.3. You can also ignore all packages with @SocketSecurity ignore-all. To ignore an alert for all future pull requests, use Socket's Dashboard to change the triage state of this alert.

Warn Critical
Critical CVE: Elliptic's private key extraction in ECDSA upon signing a malformed input (e.g. a string)

CVE: GHSA-vjh7-7g9h-fjfh Elliptic's private key extraction in ECDSA upon signing a malformed input (e.g. a string) (CRITICAL)

Affected versions: < 6.6.1

Patched version: 6.6.1

From: ?npm/ethers@5.7.2npm/elliptic@6.5.4

ℹ Read more on: This package | This alert | What is a critical CVE?

Next steps: Take a moment to review the security alert above. Review the linked package source code to understand the potential risk. Ensure the package is not malicious before proceeding. If you're unsure how to proceed, reach out to your security team or ask the Socket team for help at support@socket.dev.

Suggestion: Remove or replace dependencies that include known critical CVEs. Consumers can use dependency overrides or npm audit fix --force to remove vulnerable dependencies.

Mark the package as acceptable risk. To ignore this alert only in this pull request, reply with the comment @SocketSecurity ignore npm/elliptic@6.5.4. You can also ignore all packages with @SocketSecurity ignore-all. To ignore an alert for all future pull requests, use Socket's Dashboard to change the triage state of this alert.

View full report

devin-ai-integration[bot]

This comment was marked as resolved.

Code fixes:
- Fix operator precedence bug in utilsContract.tsx where
  "0_" + name || "tuple" never fell back to "tuple"
- Remove dead code guard in farcaster main.tsx where
  || "demo_key" fallback made the null check unreachable

README updates:
- Update git clone URLs from old standalone repos to
  getformo/examples monorepo
- Fix docs.formo.ai → docs.formo.so in with-react
- Remove stale contribution/template sections from
  with-next-page-router that referenced CJskii/next-web3-template
- Enhance with-farcaster README with Formo setup instructions
- Remove stale local SDK development section from
  with-react-native (referenced old link:../sdk-react-native)
- Update directory names in project structure sections

https://claude.ai/code/session_01Vth1WkLFrTqCTdjwZqFwby
devin-ai-integration[bot]

This comment was marked as resolved.

…rojectId

- with-farcaster: pass wagmi config and queryClient to FormoAnalyticsProvider
  options so the SDK can auto-capture wallet events (connect, disconnect, etc.)
- with-next-page-router: read WalletConnect projectId from
  NEXT_PUBLIC_REOWN_PROJECT_ID env var instead of hardcoded placeholder
- with-next-page-router: add NEXT_PUBLIC_REOWN_PROJECT_ID to .env.example

https://claude.ai/code/session_01Vth1WkLFrTqCTdjwZqFwby
devin-ai-integration[bot]

This comment was marked as resolved.

Move FormoAnalyticsProvider inside WagmiProvider and QueryClientProvider
to be consistent with the provider ordering used in with-dynamic,
with-privy, with-reown, and all other examples in the monorepo.

https://claude.ai/code/session_01Vth1WkLFrTqCTdjwZqFwby
Bumps @formo/analytics to the latest version in all 8 examples that
were behind (with-dynamic, with-farcaster, with-next-page-router,
with-porto, with-privy, with-react, with-reown, with-solana).
with-next-app-router was already at ^1.27.0.

https://claude.ai/code/session_01Vth1WkLFrTqCTdjwZqFwby
Ensures .env files are gitignored per-example so each example is
self-contained when copied or cloned independently. Each .gitignore
is tailored to its framework (Vite, Next.js, CRA, Expo).

https://claude.ai/code/session_01Vth1WkLFrTqCTdjwZqFwby
Adds files from the upstream formo-example-next-app-router repo that
were missing after migration to the monorepo:
- .husky/pre-commit hook for lint-staged
- .lintstagedrc.js (adapted for foundry instead of hardhat)
- .github/workflows/lint.yaml for CI
- CONTRIBUTING.md

https://claude.ai/code/session_01Vth1WkLFrTqCTdjwZqFwby
cursor[bot]

This comment was marked as resolved.

The *.local glob already covers .env.local, .env.development.local,
.env.test.local, and .env.production.local, making the specific
patterns unnecessary.

https://claude.ai/code/session_01Vth1WkLFrTqCTdjwZqFwby
<TxnNotification message="Waiting for transaction to complete." blockExplorerLink={blockExplorerTxURL} />,
);

transactionReceipt = await publicClient.waitForTransactionReceipt({

This comment was marked as outdated.

yosriady and others added 4 commits February 24, 2026 18:26
Port three new examples from formo-example-* repos into the monorepo:
- with-metamask: Next.js 15 + Wagmi + MetaMask SDK
- with-thirdweb: Next.js 14 + Thirdweb v4
- with-web3-onboard: Next.js 15 + Web3 Onboard

All updated to @formo/analytics ^1.27.0 with clean .gitignore
and .env.example files. Removed local SDK path references from
next.config files.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- with-metamask: fix LogLevel[] type mismatch in logger.levels
- with-farcaster: fix useSendCalls data rendering (use data.id),
  fix window.Buffer type assertion
- with-solana: remove stale symlink, add type assertions for
  unpublished solana SDK types
- with-privy: add force-dynamic to prevent SSG prerender with
  invalid Privy app ID
- with-web3-onboard: escape apostrophes in JSX, remove unused
  analytics variable
- with-next-app-router: add null check for publicClient in
  useTransactor hook
- Update lockfiles for all examples after clean install

12 of 13 examples now build successfully.
with-react-native is blocked on @formo/react-native-analytics
not being published to npm.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
console.log('Tracking custom event:', eventName, parsedProperties);

// Send the track event using the useFormo hook
await analytics.track(eventName, parsedProperties);
Copy link

Choose a reason for hiding this comment

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

Bug: The analytics object from useFormo() is used to call analytics.track() without a null check, risking a runtime error if the analytics SDK is not yet initialized.
Severity: MEDIUM

Suggested Fix

Add a null check before line 506 to ensure the analytics object is not null before attempting to call the track method. For example: if (!analytics) { setTrackError("Analytics not initialized"); return; }.

Prompt for AI Agent
Review the code at the location below. A potential bug has been identified by an AI
agent.
Verify if this is a real issue. If it is, propose a fix; if not, explain why it's not
valid.

Location: with-farcaster/src/App.tsx#L506

Potential issue: The `analytics` instance is obtained from the `useFormo()` hook.
However, `analytics.track()` is called directly without verifying if the `analytics`
object is null or undefined. While the `FormoAnalyticsProvider` wraps the application,
the `useFormo()` hook could return a nullish value if the provider has not finished
initializing. This would cause a runtime error, "Cannot read property 'track' of null".
Although a `try-catch` block exists, it would only show a generic "Unknown tracking
error" instead of handling the uninitialized state gracefully. Other examples in the
codebase include this null check, indicating it's a necessary safeguard.

yosriady and others added 2 commits February 25, 2026 09:12
Runs `build` for each example in a parallel matrix on PRs and pushes
to main. Auto-generates .env from .env.example with dummy CI values.
Skips with-solana and with-react-native until their SDK versions are
published.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
useFormo() can return undefined before the SDK finishes initializing.
Adds a guard before calling analytics.track() to surface a clear error
message instead of a cryptic runtime crash.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
functionName: abiFunction.name,
abi: abi,
args: getParsedContractFunctionArgs(form),
value: BigInt(txValue),
Copy link

Choose a reason for hiding this comment

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

Bug: The code unconditionally calls BigInt(txValue). For non-payable functions, txValue is an empty string, which causes a SyntaxError runtime crash when the transaction is sent.
Severity: CRITICAL

Suggested Fix

The value parameter should only be included if the function is payable, or it should default to a safe value. For example, change BigInt(txValue) to BigInt(txValue || "0") to handle the empty string case.

Prompt for AI Agent
Review the code at the location below. A potential bug has been identified by an AI
agent.
Verify if this is a real issue. If it is, propose a fix; if not, explain why it's not
valid.

Location:
with-next-app-router/packages/nextjs/app/debug/_components/contract/WriteOnlyFunctionForm.tsx#L53

Potential issue: When a user interacts with a non-payable contract function, the
`txValue` state remains an empty string (`""`) because its corresponding input field is
not rendered. However, when the `handleWrite` function is called, it attempts to create
the transaction parameters with `value: BigInt(txValue)`. This results in `BigInt("")`,
which throws a `SyntaxError` and causes a runtime crash, preventing interaction with any
non-payable functions on the debug page.

cursor[bot]

This comment was marked as resolved.

- Fix BigInt("") SyntaxError in WriteOnlyFunctionForm for non-payable
  contract functions by defaulting to BigInt(0)
- Use Node 22 for farcaster example (@farcaster/frame-sdk requires it)
- Use yarn --immutable for next-app-router (Yarn Berry, not classic)
- Write .env directly for next-app-router nested workspace instead of
  copying from potentially missing root .env
- Add viem to with-react deps (CRA resolves all @formo/analytics
  imports including wagmi module at build time)
- Use --legacy-peer-deps for with-thirdweb (react-query v4 vs v5
  peer conflict)
- Set CI=false in build step to avoid CRA treating warnings as errors

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@yosriady yosriady changed the title Add Formo Analytics SDK examples across multiple frameworks P-1953 Add Formo Analytics SDK examples across multiple frameworks Feb 25, 2026
@linear
Copy link

linear bot commented Feb 25, 2026

Husky postinstall fails in CI because .git is at the repo root, not
inside the example directory. Setting HUSKY=0 disables it. Also drop
--immutable for Yarn Berry since the install was failing silently.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

const handleIdentify = async () => {
if (!address) return;
await formo.identify({
Copy link

Choose a reason for hiding this comment

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

Bug: The formo object from useFormo() is used without checking if it's null. This could cause a crash if the SDK hasn't initialized yet.
Severity: HIGH

Suggested Fix

Before calling any methods on the formo object, add a check to ensure it is not null or undefined. For example: if (!formo) return; before calls like formo.track(...). This will prevent runtime errors during the SDK's initialization phase.

Prompt for AI Agent
Review the code at the location below. A potential bug has been identified by an AI
agent.
Verify if this is a real issue. If it is, propose a fix; if not, explain why it's not
valid.

Location: with-porto/src/app/page.tsx#L395

Potential issue: The `formo` object, returned by the `useFormo()` hook, is used to call
methods like `formo.identify()`, `formo.track()`, and `formo.hasOptedOutTracking()`
without any null or undefined checks. Other examples within the same repository
defensively check if the `formo` object is initialized before use. Because some of these
calls, like in the `ConsentSection` `useEffect`, run automatically on component mount,
there is a race condition. If the Formo SDK has not finished initializing, `formo` will
be null or undefined, leading to a `TypeError` and a runtime crash.

@yosriady yosriady merged commit 9bff83b into main Feb 25, 2026
15 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