react-native-sliding-blocks
v0.0.15
Published
React Native sliding blocks game library
Downloads
902
Readme
react-native-sliding-blocks
A React Native game library for building high-performance sliding block puzzles. Slide colored segments on a grid, fill rows to clear them, and watch blocks cascade. Built with Skia, Reanimated, and RxJS—60fps on iOS and Android.
This is a library package, not a standalone app. Install it in your React Native project and integrate the game with your own UI, assets, and logic.
Installation
npm install react-native-sliding-blocks
# or
yarn add react-native-sliding-blocksPeer Dependencies
Install these in your app (they are peer dependencies):
| Package | Purpose |
|---------|---------|
| react | UI framework |
| react-native | Mobile runtime |
| @shopify/react-native-skia | 2D graphics & canvas |
| react-native-reanimated | Animations |
| react-native-gesture-handler | Touch and gestures |
| react-native-worklets | UI thread worklets |
| rxjs | Reactive streams |
Requirements: Node.js ≥ 22.11.0
Quick Start
import { SlidingBlocks } from 'react-native-sliding-blocks'
function GameScreen() {
return (
<SlidingBlocks
config={{
rowsCount: 10,
columnsCount: 8,
padding: 16,
explosionPoolSize: 80,
keysSize: 5
}}
callbacks={{
onScoreChange: (score) => console.log('Score:', score),
onGameOver: (score) => console.log('Game over:', score),
onRemovingStart: () => playClearSound(),
onFitComplete: ({ hadActualFit }) => hadActualFit && playSlideSound()
}}
/>
)
}The library uses Skia-drawn blocks and a solid background by default. No assets required.
API
Declarative API — <SlidingBlocks />
All-in-one component. Pass config, callbacks, and optional assets/theme.
<SlidingBlocks
config={SlidingBlocksConfig}
callbacks?: SlidingBlocksCallbacks
assets?: SlidingBlocksAssets
theme?: Partial<SlidingBlocksTheme>
settings?: SlidingBlocksSettingsOverrides
engine?: IGameEngine
initialState?: GameStateSnapshot | null
onGameStateChange?: (state: GameStateSnapshot) => void
blockRenderMode?: 'skia' | 'image'
showFinishOption?: boolean
onLoadProgress?: (progress: number) => void
onLoadComplete?: () => void
/>| Prop | Type | Description |
|------|------|-------------|
| config | SlidingBlocksConfig | Required. Grid layout: rowsCount, columnsCount, padding, explosionPoolSize, keysSize |
| callbacks | SlidingBlocksCallbacks | Score, game over, pause, restart, sound hooks, etc. |
| assets | SlidingBlocksAssets | Block PNGs, background image. Omit for Skia fallbacks |
| theme | Partial<SlidingBlocksTheme> | Overlay, score bar, block colors |
| settings | SlidingBlocksSettingsOverrides | Block, explosion, checkerboard, explosionPresets, animations (durations in ms), feedback (opacity values). Tune for performance: explosionPresets.explosionEnabled: false disables explosion; circlesOnly: true uses circles-only particles; set animations.* to 0 to disable animations. |
| engine | IGameEngine | Optional pre-created engine |
| initialState | GameStateSnapshot \| null | Restore from persisted state; host loads from storage to resume after app kill |
| onGameStateChange | (state: GameStateSnapshot) => void | Called when state changes; host should persist (e.g. AsyncStorage). Snapshot includes gameOver flag; host typically clears when game over. |
| blockRenderMode | 'skia' \| 'image' | 'skia' = draw blocks (default), 'image' = PNG assets |
| showFinishOption | boolean | Show "Finish" in pause overlay; use with onFinish |
Imperative handle (ref)
const ref = useRef<SlidingBlocksHandle>(null)
<SlidingBlocks ref={ref} config={...} callbacks={...} />
// Then:
ref.current?.pause()
ref.current?.resume()
ref.current?.restart()
ref.current?.isPaused() // boolean
ref.current?.getGameState() // GameStateSnapshot for persistenceComposable API — useSlidingBlocks
Build custom layouts (e.g. custom score bar, different ordering).
import { useSlidingBlocks } from 'react-native-sliding-blocks'
function CustomGameScreen() {
const { Root, ScoreBar, GameArea, ref } = useSlidingBlocks({
config: { rowsCount: 10, columnsCount: 8, padding: 16, explosionPoolSize: 80, keysSize: 5 },
callbacks: { onScoreChange, onGameOver }
})
return (
<Root>
<MyCustomScoreBar />
<GameArea blockRenderMode="skia" onLoadComplete={() => setReady(true)} />
</Root>
)
}Returns:
Root— Wrapper; must wrapScoreBarandGameAreaScoreBar— Default score barGameArea— Game canvas (blocks, grid, overlays)ref—SlidingBlocksHandle(pause, resume, restart, isPaused, getGameState)
For fully custom layouts, use useComposableSlidingBlocksContext() inside Root to access layout and shared values.
Low-level API
GameRootView— Minimal wrapper when you need full control over layout and bridge.createGameEngine(config, host?, options?)— Create a React-agnostic engine for headless testing or custom integration.optionscan includeonRowAdded,animOverrides(removeFadeMs,itemDropMs),initialState(restore from persisted state), andonGameStateChange(called when state changes for host persistence).GameStateSnapshot,isSnapshotCompatible(snapshot, config),getLayoutVersion(config)— Types and helpers for game state persistence. UseisSnapshotCompatiblebefore resuming to ensure saved state matches current layout.PreloaderOverlay— Loading overlay; optionalfillAnimationDurationMsprop. Usesettings.animations.loadingBarFillMsfor consistency.scheduleIdle,cancelIdle,GESTURE_SENSITIVITY, layout constants (TOP_PAUSE, SCORE_BAR, etc.) — Exported for advanced use.
Game State Persistence
The library provides serializable game state for host-side persistence. Persistence (e.g. AsyncStorage) is the host's responsibility.
| Export | Purpose |
|--------|---------|
| GameStateSnapshot | Serializable type: rows, score, multiplier, layoutVersion, gameOver |
| getGameState() | Engine / handle method — returns current state |
| initialState prop | Pass loaded state to resume after app kill |
| onGameStateChange | Callback — persist when state changes; host typically clears when gameOver: true |
| isSnapshotCompatible(snapshot, config) | Validate saved state matches current layout before resume |
Example flow:
- On mount: load from storage → if valid and not game over → pass as
initialState. - On state change:
onGameStateChange(state)→ persist to storage. - On game over: clear stored state (host decides).
- Use
isSnapshotCompatibleso layout changes (e.g. settings) don't corrupt restore.
Configuration
SlidingBlocksConfig (game layout)
type SlidingBlocksConfig = {
rowsCount: number // e.g. 10
columnsCount: number // e.g. 8
padding: number // screen padding (px)
explosionPoolSize: number
keysSize: number // block types (colors)
}SlidingBlocksCallbacks
| Callback | When |
|----------|------|
| onScoreChange | After each row clear |
| onGameOver | Game ends |
| onPause / onResume | User pauses/resumes |
| onRestart | User restarts (from overlay) |
| onFinish | User taps "Finish" in pause overlay |
| onGestureStart / onGestureEnd | Pan starts/ends |
| onRemovingStart / onRemovingEnd | Row clear animation starts/ends; payload { hasSuper: boolean } |
| onFitStart / onFitComplete | Slide/snap animation; onFitComplete({ hadActualFit }) for slide sound (hadActualFit = true when blocks actually moved) |
| onRowAdded | New row added at top |
SlidingBlocksAssets
| Asset | Description |
|-------|-------------|
| blockImages | Map of color hex → [1×1, 1×2, 1×3, 1×4] image sources (e.g. require(...)) |
| backgroundImage | Full-screen background image source |
Omit for fallbacks: solid background, Skia-drawn blocks.
SlidingBlocksSettingsOverrides
Partial overrides merged with defaults. All optional.
| Category | Fields | Description |
|----------|--------|-------------|
| block | radius, borderWidth, borderColor, frostHighlightColor, frostHighlightHeightRatio, superGradientColors, superGradientSteps | Block appearance |
| explosion | radius, baseParticleSize, riseHeight, fallDistance, pictureSize | Explosion particles |
| checkerboard | defaultBaseColor, defaultDarkOpacity, defaultLightOpacity | Grid styling |
| explosionPresets | particleCount, trajectoryPresetCount, shapePresetCount, circlesOnly, explosionEnabled | Explosion variety. Set explosionEnabled: false to disable. circlesOnly: true for faster rendering. |
| animations | completeSnapMs, itemDropMs, willRemovePulseMs, removeFadeMs, removeExplosionMs, gameOverInMs, gameOverOutMs, pauseOverlayMs, loadingBarFillMs | Animation durations (ms). removeFadeMs = block opacity fade; removeExplosionMs = explosion particles. |
| feedback | blockIdle, willRemovePulseMin, ghostActive, indicatorActive | Opacity values (0–1) for blocks, ghost, indicator |
Performance presets are host-defined. Create presets as SlidingBlocksSettingsOverrides (e.g. blockRenderMode, explosionPresets, animations) and apply via your settings store. The example app defines four presets—extra-low, low, fine, good—in example/src/settings/performancePresets.ts and exposes them as one-tap buttons with a "Custom settings" section for manual overrides.
Game Mechanics
- Grid — Configurable columns × rows (e.g. 8×10)
- Blocks — Colored segments (1–4 cells). Drag left/right to reposition.
- Clearing — Fill a row completely to clear it; blocks above drop.
- Super segments — Rare black segments; when cleared, also wipe overlapping blocks in adjacent rows for combos.
- Score & multiplier — Animated display.
Architecture
The package uses MVVM with RxJS and Reanimated. The engine is React-agnostic; a bridge wires RxJS streams to SharedValues. No React commits during gameplay—all rendering is driven by SharedValues and Skia.
For a deep dive, see src/CONCEPTS.md.
| Layer | Path | Responsibility |
|-------|------|----------------|
| Engine | engine/ | Pure game logic, RxJS, no React |
| Bridge | bridge/ | RxJS → SharedValues binding |
| UI | ui/ | React components, Skia, SharedValues |
Project Structure
react-native-sliding-blocks/
├── src/ # Library source
│ ├── bridge/ # RxJS → SharedValues (useEngineBridge, GestureCompletionOrchestrator)
│ ├── engine/ # Game logic, RxJS, no React
│ ├── ui/ # React components, Skia, contexts
│ ├── constants/ # Layout constants, game config
│ ├── types/ # Settings, layout types
│ ├── config.ts
│ ├── CONCEPTS.md # Architecture guide
│ └── index.ts
├── example/ # Example React Native app
└── package.jsonExample App
An example app lives in example/—it demonstrates:
- Declarative and composable APIs
- Settings screen with performance presets (extra-low, low, fine, good) and a collapsible "Custom settings" section for manual overrides
- Game state persistence (AsyncStorage) with a Resume button when stored state exists
- Sounds, themes, block images
# From repo root
yarn install
yarn example # Install example deps
yarn example:start # Start Metro
yarn example:ios # Run iOS (in another terminal)
yarn example:android # Run Android (in another terminal)Developing the library? The example uses Metro's custom resolver so changes in src/ trigger hot reload. See DEVELOPMENT.md.
Requires React Native environment setup.
Scripts
| Command | Description |
|---------|-------------|
| yarn lint | Lint library source |
| yarn example | Install example dependencies |
| yarn example:start | Start Metro |
| yarn example:start:dev | Start Metro with --reset-cache (use if changes don't appear) |
| yarn example:ios | Run on iOS |
| yarn example:android | Run on Android |
Tech Stack
| Layer | Technology |
|-------|------------|
| Framework | React Native |
| Graphics | Skia (@shopify/react-native-skia) |
| Animations | React Native Reanimated |
| Gestures | React Native Gesture Handler |
| State / Streams | RxJS |
License
MIT
