Skip to main content

Performance & Build Optimization

Graz has been optimized for both runtime performance and developer experience with significant improvements to build times and package size.

Package Size​

The graz package has been highly optimized for minimal bundle impact:

  • Total package size: ~220 KB (down from 800 KB)
  • Main bundle (ESM): 52 KB (minified)
  • Main bundle (CJS): 52 KB (minified)
  • TypeScript declarations: 49 KB per format (.d.ts and .d.mts)

What This Means for You​

  • 72% smaller npm installs: Faster CI/CD pipelines and local development setup
  • Optimized for tree-shaking: Modern bundlers can eliminate unused code
  • No source maps in production: Keeps package size minimal

Build Performance​

Production Builds​

Production builds are optimized for output quality:

  • Build time: ~4 seconds
  • TypeScript declaration generation: ~3 seconds
  • Incremental builds: Cached for faster subsequent builds

Development Builds​

Development mode is optimized for speed:

  • Initial build: ~1 second
  • Hot-reload rebuilds: <500ms (nearly instant)
  • DTS generation: Skipped in watch mode for maximum speed

Development Experience​

Fast Watch Mode​

When developing with graz, use watch mode for the best experience:

# In the graz package
pnpm graz dev

# This provides:
# - Instant rebuilds (<500ms)
# - No DTS generation (saves ~3 seconds)
# - Hot module reloading

Build Optimizations Applied​

TypeScript Incremental Compilation​

Graz uses TypeScript's incremental compilation feature:

  • Caches type information between builds
  • Only recompiles changed files
  • Reduces build time by up to 70% on subsequent builds

Terser Minification​

Production builds use Terser for advanced minification:

  • Better compression than basic minification
  • Dead code elimination
  • Optimized for ES2020+ targets

Smart DTS Generation​

TypeScript declarations are generated efficiently:

  • resolve: false keeps external types external (43% smaller)
  • Skipped entirely in watch mode for instant feedback
  • Generated only for production builds

Runtime Performance​

Built on React Query​

Graz leverages @tanstack/react-query for optimal data fetching:

  • Automatic caching and request deduplication
  • Background refetching and stale-while-revalidate
  • Optimistic updates and mutations

Zustand State Management​

State is managed efficiently with Zustand:

  • Minimal re-renders (selector-based subscriptions)
  • Persistent state with localStorage/sessionStorage
  • Lightweight (~1 KB overhead)

Tree-Shaking Support​

Graz is built with tree-shaking in mind:

  • ES module exports for optimal bundler compatibility
  • sideEffects: false in package.json
  • Modular architecture allows importing only what you need

Multi-Chain Performance​

Concurrent Request Management​

Multi-chain operations are optimized with configurable concurrency:

<GrazProvider
grazOptions={{
chains: [chain1, chain2, chain3],
multiChainFetchConcurrency: 3, // Default: 3 concurrent requests
}}
>
{/* Your app */}
</GrazProvider>

Benefits:

  • Prevents overwhelming RPC endpoints
  • Balances speed with reliability
  • Configurable per application needs

Efficient Multi-Chain Utilities​

The createMultiChainAsyncFunction utility uses p-map for efficient parallel processing:

  • Controlled concurrency to prevent rate limiting
  • Promise-based for optimal async handling
  • Automatic error handling per chain

Best Practices​

For Application Developers​

  1. Use the hooks: They're optimized with React Query for caching and performance
  2. Enable stale-while-revalidate: Let React Query handle background updates
  3. Optimize re-renders: Use selector functions with useAccount and other hooks
  4. Configure concurrency: Adjust multiChainFetchConcurrency based on your needs
// Good: Use selector to avoid unnecessary re-renders
const { data: account } = useAccount({
onConnect: (data) => {
// Only runs on connect, not on every state change
},
});

// Good: Configure concurrency for your use case
<GrazProvider
grazOptions={{
chains: myChains,
multiChainFetchConcurrency: 5, // Increase if you have fast RPCs
}}
/>;

For Contributors​

  1. Use pnpm graz dev for development (instant rebuilds)
  2. Run pnpm graz build before committing (verify production build)
  3. Avoid unnecessary dependencies: Keep bundle size minimal
  4. Write tree-shakeable code: Use named exports, avoid side effects

Monitoring Performance​

Bundle Analysis​

To analyze what's in your application bundle:

# Using webpack-bundle-analyzer
npm install --save-dev webpack-bundle-analyzer

# Using source-map-explorer
npx source-map-explorer 'build/static/js/*.js'

Build Time Tracking​

Track graz build times:

# Production build with timing
time pnpm graz build

# Watch mode (note rebuild speed)
pnpm graz dev

Version History​

v0.4.0 - Build Optimizations​

Major performance improvements:

  • 25% faster production builds (5.2s → 3.9s)
  • 87% faster dev rebuilds (5.2s → 0.7s)
  • 72% smaller package (800 KB → 220 KB)
  • 43% smaller TypeScript declarations (87 KB → 49 KB)

See the changelog for details.

Future Optimizations​

Potential future improvements being considered:

Code Splitting by Wallet​

Split wallet adapters into separate entry points:

// Instead of bundling all wallets
import { getKeplr, getLeap /* all 12+ wallets */ } from "graz";

// Users could import only what they need
import { useConnect } from "graz";
import { getKeplr } from "graz/wallets/keplr";
import { getLeap } from "graz/wallets/leap";

Expected impact: 60-70% smaller bundles for most users

Dynamic Imports​

Lazy-load wallet adapters on-demand:

// Load wallet adapter only when needed
const wallet = await import(`graz/wallets/${walletType}`);

Expected impact: Faster initial page load, smaller initial bundle

Contributing​

Have ideas for performance improvements? We welcome contributions!

Questions?​

If you have questions about performance or build optimizations: