GiftingStack
Sign in Get started

Docs

SDKs

The gift tray, the gift effects and a small client for your app, on five platforms. All five follow one shared contract and run the same test fixtures for the logic: prices, tiers, combos, the client and the room protocol. The screens (tray, effects, wall) are drawn natively on each platform and are not covered by those fixtures, so they can differ in small ways.

Before you start

Web

@giftingstack/sdk/web. TypeScript, works with any framework. Also as the custom elements <gift-tray>, <gift-effect-layer>, <gift-wall>, <gift-away-summary>, <gift-leaderboard>, <gift-room> and <gift-battle-bar> (web only).

import { createGiftingClient } from "@giftingstack/sdk";
import { GiftEffectLayer, createGiftTray } from "@giftingstack/sdk/web";

const client = createGiftingClient({
  getToken: async () => (await fetch("/api/gift-token")).text(),
});
const screen = document.querySelector("#screen");
const effects = new GiftEffectLayer(screen);
const tray = createGiftTray({ container: screen, client, receiver: post.authorId, effects });

// When the screen goes away:
tray.destroy();

Try it: the live web demo.

React Native and Expo

@giftingstack/sdk/react-native. Needs react 19.1 or newer and react-native 0.80 or newer.

import { useRef } from "react";
import { createGiftingClient } from "@giftingstack/sdk";
import { GiftEffectLayer, GiftTray, useGiftTray } from "@giftingstack/sdk/react-native";

const client = createGiftingClient({
  getToken: async () => (await fetch("https://your.app/api/gift-token")).text(),
});

function PostScreen({ post }) {
  const effects = useRef(null);
  const tray = useGiftTray({ client, receiver: post.authorId, effects, reference: post.id });
  return (
    <View style={{ flex: 1 }}>
      {/* your screen */}
      <GiftTray tray={tray} />
      <GiftEffectLayer ref={effects} />
    </View>
  );
}

Flutter

Package giftingstack. Dart 3.6 or newer; depends on Flutter and http only.

final client = GiftingClient(
  getToken: ({required bool forceRefresh}) =>
      myBackend.giftingToken(forceRefresh: forceRefresh),
);
final effects = GiftEffectController();
final tray = GiftTrayNotifier(client: client, receiver: 'mateo', effects: effects);

Stack(children: [
  YourScreen(),
  Positioned.fill(
    child: GiftTray(tray: tray),
  ),
  Positioned.fill(child: GiftEffectLayer(controller: effects)),
]);

Android

Kotlin and Jetpack Compose, minSdk 24. It brings kotlinx-coroutines and kotlinx-serialization (both used in its public API) and nothing else outside AndroidX and Compose.

val client = GiftingClient(getToken = { forceRefresh -> myBackend.giftingToken(forceRefresh) })

@Composable
fun PostScreen(client: GiftingApi) {
    val effects = rememberGiftEffectLayerState()
    val tray = rememberGiftTrayHolder(client, receiver = "mateo", effects = effects)
    Box(Modifier.fillMaxSize()) {
        /* your screen */
        GiftTray(tray = tray)
        GiftEffectLayer(effects)
    }
}

iOS

Swift and SwiftUI, iOS 15+. Swift package, product GiftingStack .

import GiftingStack
import SwiftUI

let client = GiftingClient(getToken: { forceRefresh in
    try await MyBackend.shared.giftingToken(forceRefresh: forceRefresh)
})

struct PostScreen: View {
    @StateObject private var effects = GiftEffectController()
    @StateObject private var tray = GiftTrayModel(client: client, receiver: "mateo")

    var body: some View {
        ZStack {
            PostView()
            GiftTray(tray: tray)
            GiftEffectLayer(controller: effects).ignoresSafeArea()
        }
        .onAppear { tray.effects = effects; tray.start() }
        .onDisappear { tray.stop() }
    }
}

What each platform has

Same names and behaviour wherever a row says yes. The table scrolls sideways on a phone.

                          Web   RN    Flutter Android iOS
Gift tray (sheet)         yes   yes   yes     yes     yes
Gift button + keyboard    yes   yes   yes     yes     yes
Battle "Send to" picker   yes   yes   yes     yes     yes
Tray as popover / inline  yes   -     -       -       -
Effect layer              yes   yes   yes     yes     yes
Gift wall                 yes   yes   yes     yes     yes
While you were away       yes   yes   yes     yes     yes
Leaderboard               yes   yes   yes     yes     yes
Rooms (join, effects)     yes   yes   yes     yes     yes
Battle scores (data)      yes   yes   yes     yes     yes
Battle bar (drawn)        yes   yes   yes     yes     yes
Render slots              yes   yes   yes     yes     yes
Test mode badge           yes   yes   yes     yes     yes
Theme from one colour     yes   yes   yes     yes     yes
Custom elements (tags)    yes   -     -       -       -

Render slots are renderChip (wall), renderRow (leaderboard) and renderSummary (away), to draw your own item inside our container. The battle bar (GiftBattleBar and <gift-battle-bar> on web, BattleBar on React Native, GiftBattleBar on Flutter, Android and iOS) draws a versus round for you: both receivers with their scores, a countdown and a split bar that slides as gifts arrive; it follows the theme and reduced motion, and the numbers behind it (split, clock, short scores, colours) are one shared test file that all five SDKs pass. You can still draw your own bar from the battle view. The popover and inline tray placements exist on web only; the other platforms use the bottom sheet. "Gift button + keyboard" is GiftButton for your own message bar with the tray taking the keyboard's place (how).

What all SDKs do

Also in the SDKs