This guide will walk you through the process of integrating the Para SDK with TanStack Start while preserving server-side rendering (SSR) capabilities.

What is TanStack Start?

TanStack Start is a full-stack React framework powered by TanStack Router. It provides full-document SSR, streaming, server functions, bundling, and more using tools like Nitro and Vite. It’s ready to deploy to your favorite hosting provider!

TanStack Start uses Nitro and Vite to bundle and deploy your application, providing:

  • A unified API for SSR, streaming, and hydration
  • Extraction of server-only code from your client-side code (e.g., server functions)
  • Bundling your application for deployment to any hosting provider

Prerequisites

To use Para, you need an API key. This key authenticates your requests to Para services and is essential for integration.

Don’t have an API key yet? Request access to the Developer Portal to create API keys, manage billing, teams, and more.

Installing Dependencies

First, install the Para React SDK and React Query using your preferred package manager:

npm install @getpara/react-sdk @tanstack/react-query

Setting Up Polyfills with TanStack Start

Since TanStack Start uses Vite under the hood, we need to set up polyfills for modules like crypto, buffer, etc. that Para SDK relies on. However, we only want to run these polyfills on the client, not on the server.

  1. Install the Vite Node Polyfills plugin:
npm install vite-plugin-node-polyfills --save-dev
  1. Configure the polyfills in your app.config.ts:
import { defineConfig } from "@tanstack/react-start/config";
import tsConfigPaths from "vite-tsconfig-paths";
import { nodePolyfills } from "vite-plugin-node-polyfills";

export default defineConfig({
  tsr: { appDirectory: "src" },

  // Base configuration (applied to both client and server)
  vite: {
    plugins: [tsConfigPaths({ projects: ["./tsconfig.json"] })],
    define: {
      // This helps modules determine the execution environment
      "process.browser": true,
    },
  },

  // Client-specific configuration
  routers: {
    client: {
      vite: {
        // Apply node polyfills only on the client side
        plugins: [nodePolyfills()],
      },
    },
  },
});

This approach:

  • Avoids loading node polyfills on the server where they’re unnecessary and can cause conflicts
  • Sets process.browser to true which helps modules determine the execution environment
  • Ensures proper resolution of browser-specific code paths

Setting Up the Para SDK

In TanStack Start, Para SDK components must be loaded client-side only to avoid SSR conflicts. This is accomplished using a combination of React.lazy for dynamic loading and ClientOnly from TanStack Router to prevent server rendering.

1

Create Environment Constants

First, set up your API key and environment constants in a file like src/constants.ts:

export const API_KEY = import.meta.env.VITE_PARA_API_KEY || "";
export const ENVIRONMENT = import.meta.env.VITE_PARA_ENVIRONMENT || "BETA";

Para offers two hosted environments: Environment.BETA (alias Environment.DEVELOPMENT) for testing, and Environment.PROD (alias Environment.PRODUCTION) for live use. Select the environment that matches your current development phase.

2

Create a Providers Component

Create a providers component that will wrap your application with both the QueryClientProvider and lazy-loaded ParaProvider:

import React from "react";
import { QueryClient, QueryClientProvider } from "@tanstack/react-query";
import { ClientOnly } from "@tanstack/react-router";
import { API_KEY, ENVIRONMENT } from "~/constants";

const queryClient = new QueryClient();

// Lazy load ParaProvider to avoid SSR issues
const LazyParaProvider = React.lazy(() => 
  import("@getpara/react-sdk").then((mod) => ({ 
    default: mod.ParaProvider 
  }))
);

export default function Providers({ children }: React.PropsWithChildren) {
  return (
    <ClientOnly fallback={null}>
      <QueryClientProvider client={queryClient}>
        <LazyParaProvider paraClientConfig={{ apiKey: API_KEY, env: ENVIRONMENT }}>
          {children}
        </LazyParaProvider>
      </QueryClientProvider>
    </ClientOnly>
  );
}

The ClientOnly component ensures that the ParaProvider is only rendered on the client side, preventing hydration mismatches. The fallback prop determines what to show during server-side rendering.

3

Wrap Your App with Providers

Now, wrap your root component with the Providers component:

import Providers from "~/components/Providers";

function RootComponent() {
  return (
    <html>
      <head>
        <HeadContent />
      </head>
      <body>
        <Providers>
          <main>
            <Outlet />
          </main>
        </Providers>
        <Scripts />
      </body>
    </html>
  );
}
4

Lazy Load Para Components

For any component that uses Para SDK components, use the same approach with React.lazy and ClientOnly:

import React from "react";
import { createFileRoute, ClientOnly } from "@tanstack/react-router";
import "@getpara/react-sdk/styles.css";

// Lazy load Para components
const LazyParaContainer = React.lazy(() =>
  import("~/components/ParaContainer").then((mod) => ({
    default: mod.ParaContainer,
  }))
);

function Home() {
  return (
    <main className="flex flex-col items-center justify-center min-h-screen gap-6 p-8">
      <h1 className="text-2xl font-bold">Para Modal Example</h1>
      
      <ClientOnly fallback={<p className="text-center">Loading Para components...</p>}>
        <LazyParaContainer />
      </ClientOnly>
    </main>
  );
}

export const Route = createFileRoute("/")({
  component: Home,
});

And in your ParaContainer.tsx:

import { AuthLayout, OAuthMethod, ParaModal, useModal, useWallet, useAccount } from "@getpara/react-sdk";

export function ParaContainer() {
  const { openModal } = useModal();
  const { data: wallet } = useWallet();
  const { data: account, isLoading, error } = useAccount();

  return (
    <>
      {account?.isConnected ? (
        <div>Wallet address: {wallet?.address}</div>
      ) : (
        <p>You are not logged in.</p>
      )}

      <button
        disabled={isLoading}
        onClick={openModal}
        className="rounded px-4 py-2 bg-blue-600 text-white">
        Open Para Modal
      </button>

      {error && <p className="text-red-500">{error.message}</p>}

      <ParaModal
        appName="Para Modal Example"
        logo="/para.svg"
        theme={{
          foregroundColor: "#2D3648",
          backgroundColor: "#FFFFFF",
          accentColor: "#0066CC",
          mode: "light",
        }}
      />
    </>
  );
}

Beta Testing Credentials In the BETA Environment, you can use any email ending in @test.getpara.com (like dev@test.getpara.com) or US phone numbers (+1) in the format (area code)-555-xxxx (like (425)-555-1234). Any OTP code will work for verification with these test credentials. These credentials are for beta testing only. You can delete test users anytime in the beta developer console to free up user slots.

Why Client-side Only Loading?

The Para SDK uses styled-components internally which can cause issues during server-side rendering. By using React.lazy and ClientOnly, we ensure Para components are only evaluated in the browser environment where styled-components works correctly.

Customizing the Para Modal

To provide the best experience for your users, you can customize the appearance of the Para Modal to match your application’s branding:

<ParaModal
  appName="Your App Name"
  logo="/logo.png"
  theme={{
    foregroundColor: "#2D3648",
    backgroundColor: "#FFFFFF",
    accentColor: "#0066CC",
    darkForegroundColor: "#E8EBF2",
    darkBackgroundColor: "#1A1F2B",
    darkAccentColor: "#4D9FFF",
    mode: "light",
    borderRadius: "none",
    font: "Inter",
  }}
  oAuthMethods={[
    OAuthMethod.GOOGLE,
    OAuthMethod.TWITTER,
    OAuthMethod.DISCORD,
  ]}
/>

For a full list of available ParaModalProps, refer to the customization guide:

Examples

For practical implementation of the Para SDK in TanStack Start applications, explore our example repository:

Troubleshooting

Next Steps

After integrating Para, you can explore other features and integrations to enhance your Para experience.