smithery.ai

dojo-react

Use for React integration patterns and best practices in dojo.js. DojoSdkProvider, useDojoSDK, react patterns

First seen Apr 7, 2026

Installation

$ npx skills add https://smithery.ai

Similar popular skills

Related neighbors and high-traction skills in the same topics — useful to compare before installing.

Also in this package

Other skills from smithery.ai · top by installs.

npx skills add https://smithery.ai

Browse all from smithery.ai

More details

Agent compatibility

Declared targets from SKILL.md / docs. Unmarked agents are not listed — the skill may still install via the CLI.

Claude Code Not declared
Cursor Not declared
Codex Not declared
GitHub Copilot Not declared
Windsurf Not declared
Gemini CLI Not declared
Cline Not declared
OpenCode Not declared

Package contents

Files included with this skill beyond the listing page.

  • skill md SKILL.md 6,496 B
  • docs SUMMARY.md 214 B

History

  1. First seen on skills.sh
  2. First recorded snapshot · 1 installs

SKILL.md

Dojo.js React Patterns

When to Use

Use this skill when:

  • Setting up React provider hierarchy
  • Using Effect atoms for complex async state
  • Implementing infinite scroll
  • Handling loading/error states

Provider Hierarchy

Recommended order:

import { StarknetConfig } from "@starknet-react/core";
import { DojoSdkProvider } from "@dojoengine/sdk/react";

function Providers({ children }) {
    return (
        <StarknetConfig connectors={connectors}>
            <DojoSdkProvider
                dojoConfig={dojoConfig}
                sdk={sdk}
                clientFn={createClient}
            >
                {children}
            </DojoSdkProvider>
        </StarknetConfig>
    );
}

useDojoSDK Hook

import { useDojoSDK } from "@dojoengine/sdk/react";

function GameComponent() {
    const {
        sdk,          // SDK instance
        config,       // DojoConfig
        client,       // Client from clientFn
        provider,     // DojoProvider
        useDojoStore  // Zustand store hook
    } = useDojoSDK();
}

Effect Atoms (Recommended)

Effect atoms provide robust async state management:

Entity Query Atom

import { createEntityQueryAtom } from "@dojoengine/react/effect";
import { Result } from "@dojoengine/react/effect";

const playersAtom = createEntityQueryAtom(
    runtime,
    new ToriiQueryBuilder().addEntityModel("game-Player").withLimit(100)
);

function PlayerList() {
    const players = useAtomValue(playersAtom);

    return Result.match(players, {
        onSuccess: ({ value }) => (
            <ul>
                {value.items.map(p => <li key={p.entityId}>{p.models.game.Player.name}</li>)}
            </ul>
        ),
        onFailure: (error) => <div>Error: {error.message}</div>,
        onInitial: () => <div>Loading...</div>
    });
}

Entity Updates Atom (Real-time)

import { createEntityUpdatesAtom } from "@dojoengine/react/effect";

const updatesAtom = createEntityUpdatesAtom(
    runtime,
    KeysClause(["game-Player"], [], "VariableLen").build()
);

Combined Query + Updates

import { createEntityQueryWithUpdatesAtom } from "@dojoengine/react/effect";

const livePlayersAtom = createEntityQueryWithUpdatesAtom(
    runtime,
    query,
    clause
);

Infinite Scroll

import { createEntitiesInfiniteScrollAtom } from "@dojoengine/react/effect";

const infinitePlayersAtom = createEntitiesInfiniteScrollAtom(
    runtime,
    new ToriiQueryBuilder().addEntityModel("game-Player"),
    20 // page size
);

function InfinitePlayerList() {
    const [state, loadMore] = useAtom(infinitePlayersAtom);

    return Result.match(state, {
        onSuccess: ({ value }) => (
            <>
                <ul>
                    {value.items.map(p => (
                        <li key={p.entityId}>{p.models.game.Player.name}</li>
                    ))}
                </ul>
                {value.hasMore && (
                    <button onClick={loadMore}>Load More</button>
                )}
            </>
        ),
        onFailure: (error) => <div>Error: {error.message}</div>,
        onInitial: () => <div>Loading...</div>
    });
}

Token Balance Atoms

import {
    createTokenBalanceQueryAtom,
    createTokenBalanceUpdatesAtom
} from "@dojoengine/react/effect";

// One-time query
const balanceAtom = createTokenBalanceQueryAtom(runtime, {
    contractAddresses: ["0x..."],
    accountAddresses: [playerAddress]
});

// Polling updates
const liveBalanceAtom = createTokenBalanceUpdatesAtom(
    runtime,
    { contractAddresses: ["0x..."], accountAddresses: [playerAddress] },
    5000 // poll every 5 seconds
);

Data Formatters

Transform data before rendering:

const formatters = {
    models: {
        "game-Player": (player) => ({
            ...player,
            displayName: player.name || "Anonymous",
            displayScore: `${player.score.toLocaleString()} pts`
        })
    },
    fields: {
        "game-Player.score": (score) => Math.floor(score / 100)
    }
};

const playersAtom = createEntityQueryAtom(runtime, query, formatters);

Custom Subscription Hooks

import { createSubscriptionHook } from "@dojoengine/sdk/react";

const usePlayerSubscription = createSubscriptionHook({
    subscribeMethod: (sdk, params) => sdk.subscribeEntityQuery(params),
    processInitialData: (data) => data.items,
    processUpdateData: (update) => update
});

function PlayerTracker({ entityId }) {
    const { data, error, isLoading } = usePlayerSubscription({
        query: new ToriiQueryBuilder()
            .withClause(KeysClause(["game-Player"], [entityId]).build())
    });
}

Zustand Selectors

Optimize re-renders with selectors:

function PlayerScore({ entityId }) {
    const { useDojoStore } = useDojoSDK();

    // Only re-render when score changes
    const score = useDojoStore(
        state => state.entities[entityId]?.models?.game?.Player?.score,
        (a, b) => a === b // equality function
    );

    return <span>{score}</span>;
}

useEntityId Hook

import { useEntityId } from "@dojoengine/sdk/react";

function PlayerCard({ address, gameId }) {
    // Memoized entity ID computation
    const entityId = useEntityId(address, gameId);
    const player = useModel(entityId, "game-Player");
}

Error Boundaries

import { ErrorBoundary } from "react-error-boundary";

function GameErrorFallback({ error, resetErrorBoundary }) {
    return (
        <div>
            <p>Something went wrong: {error.message}</p>
            <button onClick={resetErrorBoundary}>Retry</button>
        </div>
    );
}

function App() {
    return (
        <ErrorBoundary FallbackComponent={GameErrorFallback}>
            <Game />
        </ErrorBoundary>
    );
}

Common Pitfalls

  1. Missing provider: Ensure DojoSdkProvider wraps all Dojo-using components
  2. Effect runtime: Create runtime once at app startup, not in components
  3. Selector stability: Use stable selector functions to prevent re-renders
  4. Cleanup: Effect atoms handle cleanup, but manual subscriptions need cleanup
  5. Result.match: Always handle all three cases (success, failure, initial)