[{"data":1,"prerenderedAt":-1},["ShallowReactive",2],{"project-96477":3},{"id":4,"name":5,"fullName":6,"owner":7,"repo":5,"description":8,"homepage":9,"htmlUrl":10,"language":11,"languages":10,"totalLinesOfCode":10,"stars":12,"forks":13,"watchers":14,"openIssues":15,"contributorsCount":15,"subscribersCount":15,"size":15,"stars1d":16,"stars7d":16,"stars30d":16,"stars90d":15,"forks30d":15,"starsTrendScore":17,"compositeScore":18,"rankGlobal":10,"rankLanguage":10,"license":19,"archived":20,"fork":20,"defaultBranch":21,"hasWiki":20,"hasPages":20,"topics":22,"createdAt":10,"pushedAt":10,"updatedAt":23,"readmeContent":24,"aiSummary":10,"trendingCount":15,"starSnapshotCount":15,"syncStatus":25,"lastSyncTime":26,"discoverSource":27},96477,"expo-dynamic-notifications","rit3zh\u002Fexpo-dynamic-notifications","rit3zh","💧 Dynamic Island–style in-app notifications for React Native.","",null,"TypeScript",125,8,101,0,24,72,2.86,"MIT License",false,"main",[],"2026-09-21 02:04:32","# expo-dynamic-notifications\n\n**Dynamic Island–style** in-app notifications for React Native.\n\n---\n\n## ⚙️ Installation\n\n```bash\ngit clone https:\u002F\u002Fgithub.com\u002Frit3zh\u002Fexpo-dynamic-notifications\ncd expo-dynamic-notifications\nbunx expo prebuild\nbun install\nbun ios\n```\n\nPeer dependencies (already set up in this template):\n\n```bash\nbun add @shopify\u002Freact-native-skia react-native-reanimated react-native-worklets react-native-gesture-handler react-native-safe-area-context expo-blur expo-image expo-symbols\n```\n\n---\n\n## 🚀 Usage\n\nWrap your app once in `\u003CDynamicNotifications>`, inside `GestureHandlerRootView` and `SafeAreaProvider`. The overlay renders above everything else in the tree.\n\n```tsx\n\u002F\u002F app\u002F_layout.tsx\nimport { DynamicNotifications } from \"@\u002Fcomponents\";\nimport { Stack } from \"expo-router\";\nimport { GestureHandlerRootView } from \"react-native-gesture-handler\";\nimport { SafeAreaProvider } from \"react-native-safe-area-context\";\n\nexport default function RootLayout() {\n  return (\n    \u003CGestureHandlerRootView style={{ flex: 1 }}>\n      \u003CSafeAreaProvider>\n        \u003CDynamicNotifications>\n          \u003CStack screenOptions={{ headerShown: false }} \u002F>\n        \u003C\u002FDynamicNotifications>\n      \u003C\u002FSafeAreaProvider>\n    \u003C\u002FGestureHandlerRootView>\n  );\n}\n```\n\nThen trigger a notification from any screen:\n\n```tsx\nimport { useDynamicNotifications } from \"@\u002Fhooks\";\nimport { Button } from \"react-native\";\n\nexport function Example() {\n  const { trigger } = useDynamicNotifications();\n\n  return (\n    \u003CButton\n      title=\"Notify\"\n      onPress={() =>\n        trigger({\n          title: \"New Follower!\",\n          message: \"Sasuke followed you\",\n          avatar: \"https:\u002F\u002Frandomuser.me\u002Fapi\u002Fportraits\u002Fmen\u002F32.jpg\",\n          symbol: {\n            ios: \"person.fill.badge.plus\",\n            android: \"person_add\",\n            web: \"person_add\",\n          },\n          accent: \"#2F9BFF\",\n        })\n      }\n    \u002F>\n  );\n}\n```\n\n## Preview\n\nhttps:\u002F\u002Fgithub.com\u002Fuser-attachments\u002Fassets\u002F9c38cf28-fa30-40cc-ac24-518a85f9a055\n\n### Controlling duration\n\n`duration` is in milliseconds. Set it on the root for every notification, or on a single notification to override the root value. `null` keeps the notification visible until it's swiped away, tapped, or dismissed in code.\n\n```tsx\n\u003CDynamicNotifications duration={2500}>{\u002F* … *\u002F}\u003C\u002FDynamicNotifications>;\n\ntrigger({ title: \"Uploading…\", duration: null });\n\n\u002F\u002F later\nconst { dismiss } = useDynamicNotifications();\ndismiss();\n```\n\n### Handling taps and dismissals\n\n```tsx\ntrigger({\n  title: \"Kakashi\",\n  message: \"Training at 6. Do not be late.\",\n  onPress: () => router.push(\"\u002Fmessages\u002Fkakashi\"),\n});\n\n\u003CDynamicNotifications onDismiss={(n) => console.log(\"dismissed\", n.id)}>\n  {\u002F* … *\u002F}\n\u003C\u002FDynamicNotifications>;\n```\n\n### Custom content\n\nPass `render` to replace the default body. Your content fills the card and still gets the morph, the blur reveal and the dismiss gesture.\n\n```tsx\ntrigger({\n  title: \"Now Playing\",\n  duration: null,\n  render: () => (\n    \u003CView\n      style={{\n        flex: 1,\n        flexDirection: \"row\",\n        alignItems: \"center\",\n        paddingHorizontal: 16,\n      }}\n    >\n      \u003CText style={{ fontWeight: \"700\" }}>Midnight City: M83\u003C\u002FText>\n    \u003C\u002FView>\n  ),\n});\n```\n\nThe default layout is also exported as `\u003CDynamicNotifications.Body notification={n} \u002F>`, so you can wrap or extend it.\n\n---\n\n## 🧱 Component Anatomy\n\n```tsx\n\u003CDynamicNotifications>\n  {\u002F* your app *\u002F}\n  \u003CDynamicNotifications.Overlay>\n    {\" \"}\n    {\u002F* rendered automatically *\u002F}\n    \u003CDynamicNotifications.Gooey \u002F> {\u002F* Skia island, neck and droplet *\u002F}\n    \u003CDynamicNotifications.Content>\n      {\" \"}\n      {\u002F* the card, gesture and blur *\u002F}\n      \u003CDynamicNotifications.Body \u002F>\n    \u003C\u002FDynamicNotifications.Content>\n  \u003C\u002FDynamicNotifications.Overlay>\n\u003C\u002FDynamicNotifications>\n```\n\n---\n\n## 🧩 API\n\n### `\u003CDynamicNotifications>` (root)\n\n| Prop           | Type                   | Default                  | Description                                              |\n| -------------- | ---------------------- | ------------------------ | -------------------------------------------------------- |\n| `duration`     | `number \\| null`       | `3600`                   | Auto-dismiss delay in ms. `null` disables auto-dismiss.  |\n| `onDismiss`    | `(n) => void`          | none                     | Called after a notification finishes its exit animation. |\n| `strength`     | `number`               | `0.62`                   | Goo strength from 0 to 1. Maps to the blur radius.       |\n| `blur`         | `number`               | derived from `strength`  | Explicit goo blur radius. Overrides `strength`.          |\n| `gain`         | `number`               | `22`                     | Alpha gain of the goo colour matrix.                     |\n| `threshold`    | `number`               | `0.43`                   | Alpha cutoff of the goo colour matrix.                   |\n| `islandWidth`  | `number`               | `126`                    | Width of the island pill.                                |\n| `islandHeight` | `number`               | `37.33`                  | Height of the island pill.                               |\n| `islandTop`    | `number`               | derived from safe area   | Top offset of the island.                                |\n| `islandColor`  | `string`               | `#000000`                | Island and droplet start colour.                         |\n| `cardWidth`    | `number`               | `min(width - 32, 396)`   | Width of the notification card.                          |\n| `cardHeight`   | `number`               | `74`                     | Height of the notification card.                         |\n| `cardRadius`   | `number`               | `cardHeight \u002F 2`         | Corner radius of the card.                               |\n| `cardColor`    | `string`               | `#FFFFFF`                | Card and droplet end colour.                             |\n| `shadowColor`  | `string`               | `rgba(16, 19, 28, 0.20)` | Card drop-shadow colour.                                 |\n| `accent`       | `string`               | `#2F9BFF`                | Default title and symbol tint.                           |\n| `gap`          | `number`               | `34`                     | Space between the island and the card.                   |\n| `style`        | `StyleProp\u003CViewStyle>` | none                     | Style for the root container.                            |\n| `children`     | `ReactNode`            | none                     | Your app.                                                |\n\n### Notification object (`IDynamicNotification`)\n\n| Field      | Type                      | Description                                               |\n| ---------- | ------------------------- | --------------------------------------------------------- |\n| `title`    | `string`                  | Required. Bold first line, tinted with `accent`.          |\n| `message`  | `string`                  | Secondary line.                                           |\n| `avatar`   | `string`                  | Image URI shown on the left.                              |\n| `symbol`   | `SymbolViewProps[\"name\"]` | SF Symbol \u002F Material icon shown on the right.             |\n| `accent`   | `string`                  | Per-notification tint. Overrides the root `accent`.       |\n| `duration` | `number \\| null`          | Per-notification lifetime. Overrides the root `duration`. |\n| `onPress`  | `() => void`              | Runs on tap, then the notification dismisses.             |\n| `render`   | `(n) => ReactNode`        | Replaces the default body with your own content.          |\n| `id`       | `string`                  | Your identifier, passed back through `onDismiss`.         |\n\n### `\u003CDynamicNotifications.Gooey>`\n\nEach prop overrides the matching value from the root: `blur`, `gain`, `threshold`, `islandColor`, `cardColor`, `shadowColor`.\n\n### `\u003CDynamicNotifications.Content>` · `\u003CDynamicNotifications.Overlay>`\n\n| Prop    | Type                   | Description                       |\n| ------- | ---------------------- | --------------------------------- |\n| `style` | `StyleProp\u003CViewStyle>` | Extra style merged onto the view. |\n\n### `useDynamicNotifications()`\n\n| Field                                           | Type                                | Description                                                          |\n| ----------------------------------------------- | ----------------------------------- | -------------------------------------------------------------------- |\n| `trigger(n)`                                    | `(n: IDynamicNotification) => void` | Show a notification, replacing the current one if one is visible.    |\n| `dismiss()`                                     | `() => void`                        | Close the current notification and clear anything queued.            |\n| `isVisible`                                     | `boolean`                           | Whether a notification is currently showing.                         |\n| `notification`                                  | `IDynamicNotification \\| null`      | The active notification.                                             |\n| `layout`                                        | `INotificationLayout`               | Resolved island and card geometry.                                   |\n| `drop` · `expand` · `reveal` · `tint` · `dragY` | `SharedValue\u003Cnumber>`               | Read-only animation drivers, for UI that reacts to the notification. |\n\n---\n\n## 🧱 Stack\n\n[Expo SDK 57](https:\u002F\u002Fexpo.dev\u002Fchangelog) · [React Native 0.86](https:\u002F\u002Freactnative.dev\u002F) · [Skia](https:\u002F\u002Fshopify.github.io\u002Freact-native-skia\u002F) · [Reanimated 4](https:\u002F\u002Fdocs.swmansion.com\u002Freact-native-reanimated\u002F) · [Worklets](https:\u002F\u002Fdocs.swmansion.com\u002Freact-native-worklets\u002F) · [Gesture Handler 2](https:\u002F\u002Fdocs.swmansion.com\u002Freact-native-gesture-handler\u002F) · [expo-blur](https:\u002F\u002Fdocs.expo.dev\u002Fversions\u002Flatest\u002Fsdk\u002Fblur-view\u002F) · [expo-symbols](https:\u002F\u002Fdocs.expo.dev\u002Fversions\u002Flatest\u002Fsdk\u002Fsymbols\u002F) · [Safe Area Context](https:\u002F\u002Fgithub.com\u002Fth3rdwave\u002Freact-native-safe-area-context) · [Expo Router](https:\u002F\u002Fdocs.expo.dev\u002Frouter\u002Fintroduction\u002F)\n\n---\n",2,"2026-09-20 02:30:11","CREATED_QUERY"]