npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@pushframe/sdk

v0.1.1

Published

Pushframe React Native SDK — Server-Driven UI rendering engine

Downloads

161

Readme

@pushframe/sdk

React Native SDK for PushFrame — server-driven UI rendering engine that fetches and renders UI schemas at runtime without app updates.

Installation

npm install @pushframe/sdk
# or
yarn add @pushframe/sdk

Peer dependencies (must be installed in your project):

npm install react react-native

Optionally, if you use react-native-safe-area-context, the safeareaview component will automatically prefer it over the React Native core fallback.


Quick Start

Wrap your app with PushFrame.Provider, then drop PushFrame.Screen or PushFrame.Component wherever you want server-driven UI to appear.

import { PushFrame } from '@pushframe/sdk';

export default function App() {
  return (
    <PushFrame.Provider apiKey="your-api-key" appVersion="1.0.0">
      <PushFrame.Screen id="home" />
    </PushFrame.Provider>
  );
}

Provider

PushFrame.Provider is the root wrapper. Place it at the top of your component tree.

<PushFrame.Provider
  apiKey="your-api-key"
  appVersion="1.0.0"
  baseUrl="https://api.pushframe.io"
  context={{ user: { name: 'Alice', isAdmin: true } }}
  components={{ MyCustomCard }}
  loadingComponent={<MySpinner />}
  fallbackComponent={<MyErrorView />}
  onAction={(action, payload) => {
    if (action === 'navigate') {
      navigation.navigate(payload.screen);
    }
  }}
  onError={(error) => console.error(error)}
>
  {/* your app */}
</PushFrame.Provider>

Provider Props

| Prop | Type | Required | Default | Description | |------|------|----------|---------|-------------| | apiKey | string | Yes | — | API key used for authentication (Authorization and x-project-key headers). | | appVersion | string | No | undefined | Semver version string sent in fetch URL path. When omitted, the literal "null" is sent. Pass this explicitly — auto-detection is not supported. | | baseUrl | string | No | https://api.pushframe.io | Override the API base URL. | | context | Record<string, unknown> | No | {} | Global runtime data available to all binding expressions. | | components | Record<string, React.ComponentType> | No | {} | Register custom components by type name, available to the renderer. | | loadingComponent | ReactNode | No | null | Shown while a schema is being fetched. | | fallbackComponent | ReactNode | No | null | Shown when a schema fetch fails. | | onAction | (action: string, payload?) => void | No | — | Called for any action that is not handled internally (see Actions). | | onError | (error: Error) => void | No | — | Called when a schema fetch or render error occurs. | | children | ReactNode | Yes | — | Your application content. |


Slot Components

PushFrame.Screen

Fetches and renders a full-page screen schema. Uses flex: 1 layout.

<PushFrame.Screen
  id="home"
  context={{ currentTab: 'feed' }}
  onAction={(action, payload) => {
    if (action === 'navigate') {
      navigation.navigate(payload.screen);
      return true; // stop bubbling to Provider
    }
  }}
/>

Fetches from: GET {baseUrl}/screens/{id}/{appVersion}

PushFrame.Component

Fetches and renders an inline component schema. Renders inline without forced flex.

<PushFrame.Component
  id="product-card"
  context={{ productId: '123' }}
/>

Fetches from: GET {baseUrl}/components/{id}/{appVersion}

Slot Props

Both components share these props:

| Prop | Type | Required | Description | |------|------|----------|-------------| | id | string | Yes | Screen or component ID. | | context | Record<string, unknown> | No | Local context merged with (and overriding) the Provider context. | | isLoading | boolean | No | Externally controlled loading state. | | loadingComponent | ReactNode | No | Overrides the Provider-level loading UI. | | fallbackComponent | ReactNode | No | Overrides the Provider-level fallback UI. | | onAction | (action, payload?) => boolean \| void | No | Local action handler. Return true to stop the action from bubbling to the Provider. |


Schema Format

The API server must return schemas in this envelope format:

{
  "schema": {
    "type": "view",
    "props": { "style": { "flex": 1, "padding": 16 } },
    "children": [
      {
        "type": "text",
        "props": { "value": "Hello {{user.name}}" }
      }
    ]
  }
}

SchemaNode

interface SchemaNode {
  id?: string;
  type: string;                        // Component type (see built-in types below)
  props?: Record<string, unknown>;     // Props passed to the component
  children?: SchemaNode[];             // Nested nodes
  actions?: Action[];                  // Event → action mappings
  if?: string;                         // Binding expression; falsy = node is hidden
}

interface Action {
  trigger: string;   // e.g. "onPress", "onChange", "onLongPress"
  action: string;    // Action name (built-in or custom)
  payload?: Record<string, unknown>;
}

Built-in Component Types

| Type | React Native Equivalent | Notes | |------|------------------------|-------| | view | View | Layout container | | scrollview | ScrollView | Scrollable container; supports scroll-to action | | text | Text | Use value prop for text content | | image | Image | Use src prop for URI strings | | pressable | Pressable | Triggers onPress, onLongPress | | textinput | TextInput | onChangeonChangeText, onSubmitonSubmitEditing | | flatlist | FlatList | Requires items array and renderItem node (see below) | | modal | Modal | | | activityindicator | ActivityIndicator | Spinner | | switch | Switch | onChangeonValueChange | | keyboardavoidingview | KeyboardAvoidingView | | | safeareaview | SafeAreaView | Prefers react-native-safe-area-context when available | | statusbar | StatusBar | |


Data Binding

Props support {{expression}} syntax resolved against the runtime context.

Full binding — resolves to the actual value (any type):

{ "type": "text", "props": { "value": "{{user.name}}" } }

Inline binding — string interpolation:

{ "type": "text", "props": { "value": "Hello, {{user.name}}!" } }

Nested paths:

{ "type": "image", "props": { "src": "{{product.imageUrl}}" } }

Unresolved paths silently return undefined — they never cause a crash.


Conditional Rendering

Use the if field with a binding expression to show or hide a node:

{
  "type": "text",
  "props": { "value": "Admin Panel" },
  "if": "{{user.isAdmin}}"
}

Falsy values (false, null, undefined, 0, "") hide the node and its entire subtree.


Actions

Actions link UI events to behaviour.

{
  "type": "pressable",
  "actions": [
    {
      "trigger": "onPress",
      "action": "show-toast",
      "payload": { "message": "Saved!", "type": "success" }
    }
  ],
  "children": [
    { "type": "text", "props": { "value": "Save" } }
  ]
}

Built-in Actions

These are handled internally and do not reach your onAction callback.

| Action | Payload | |--------|---------| | show-toast | message: string, duration?: number (ms), type?: "success" \| "error" \| "info" \| "warning" | | show-bottom-sheet | schema: SchemaNode, context?: Record<string, unknown> | | dismiss-bottom-sheet | (none) | | scroll-to | x?: number, y?: number, animated?: boolean |

All other action names bubble to the slot's onAction, then to the Provider's onAction if the slot handler does not return true.


FlatList

flatlist nodes require an items array in props and a renderItem node as a sibling field (not inside children). The renderer injects { item, index } into the context for each rendered item.

{
  "type": "flatlist",
  "props": {
    "items": "{{products}}",
    "direction": "vertical"
  },
  "renderItem": {
    "type": "view",
    "props": { "style": { "padding": 8 } },
    "children": [
      { "type": "text", "props": { "value": "{{item.name}}" } },
      { "type": "text", "props": { "value": "{{item.price}}" } }
    ]
  }
}

| Prop | Type | Description | |------|------|-------------| | items | array \| string | Array of data items (or binding expression resolving to one). | | direction | "vertical" \| "horizontal" | Maps to the horizontal prop. Default: "vertical". | | numColumns | number | Passed through to FlatList. | | keyExtractor | string | Binding expression evaluated per item (e.g. "{{item.id}}"). |


Custom Components

Register your own React Native components and reference them by type name in schemas.

import { PushFrame } from '@pushframe/sdk';

function ProductCard({ title, price }: { title: string; price: string }) {
  return (
    <View>
      <Text>{title}</Text>
      <Text>{price}</Text>
    </View>
  );
}

<PushFrame.Provider
  apiKey="..."
  components={{ 'product-card': ProductCard }}
>
  {/* Schema can now use "type": "product-card" */}
</PushFrame.Provider>

Built-in type names are reserved and cannot be overridden.


Context Merging

Context flows from Provider → Slot, with the Slot's context taking precedence on key conflicts.

// Provider sets global context
<PushFrame.Provider context={{ user: { name: 'Alice' }, theme: 'dark' }}>
  {/* Slot adds/overrides local context */}
  <PushFrame.Screen
    id="profile"
    context={{ pageTitle: 'My Profile', theme: 'light' }} // overrides theme
  />
</PushFrame.Provider>

TypeScript

The SDK is written in TypeScript. Key types are exported for use in your own code:

import type {
  SchemaNode,
  Action,
  PushFrameProviderProps,
  PushFrameSlotProps,
  ToastPayload,
  BottomSheetPayload,
} from '@pushframe/sdk';

Advanced: Accessing Context

Use the usePushFrameContext hook inside any component rendered within PushFrame.Provider to access SDK internals.

import { usePushFrameContext } from '@pushframe/sdk';

function MyButton() {
  const { showToast } = usePushFrameContext();
  return (
    <Button onPress={() => showToast({ message: 'Hello!', type: 'info' })} />
  );
}

API Reference

Request Format

Every schema fetch includes these headers:

Authorization: Bearer {apiKey}
x-project-key: {apiKey}
Accept: application/json

IDs and appVersion are encodeURIComponent-encoded in the URL path. When appVersion is not set on the Provider, the literal string "null" is sent (e.g. GET /screens/home/null).

Response Format

{
  "schema": { },
  "version": 28,
  "status": "published",
  "publishedAt": "2024-01-01T00:00:00Z",
  "targetMinVersion": "0.0.0"
}

The SDK extracts response.schema automatically. A bare SchemaNode (without the envelope) is also accepted as a fallback.


License

MIT