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
- Use the hooks: They're optimized with React Query for caching and performance
- Enable stale-while-revalidate: Let React Query handle background updates
- Optimize re-renders: Use selector functions with
useAccount
and other hooks - 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
- Use
pnpm graz dev
for development (instant rebuilds) - Run
pnpm graz build
before committing (verify production build) - Avoid unnecessary dependencies: Keep bundle size minimal
- 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.3.7 - 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!
- See BUILD_OPTIMIZATION_ANALYSIS.md for detailed analysis
- Check Contributing Guide for development setup
- Open an issue or PR with your optimization ideas
Questions?
If you have questions about performance or build optimizations:
- Check our GitHub Discussions
- Open an issue for bugs
- Join the conversation in our community channels