{"owner":"infinitered","repo":"reactotron","hasSkills":true,"totalSkillsCount":16,"totalTokensCount":6159,"categories":["root-instruction","claude-rule","anthropic-skill"],"hasMcp":false,"mcpConfig":null,"found":["AGENTS.md","CLAUDE.md","docs/plugins/_category_.json","docs/plugins/apisauce.md","docs/plugins/async-storage.md","docs/plugins/benchmark.md","docs/plugins/index.md","docs/plugins/mst.md","docs/plugins/networking.md","docs/plugins/open-in-editor.md","docs/plugins/overlay.md","docs/plugins/react-native-mmkv.md","docs/plugins/redux.md","docs/plugins/storybook.md","docs/plugins/track-global-errors.md","docs/plugins/track-global-logs.md"],"skills":{"AGENTS.md":"# Agent Notes\n\nMinimal router — the linked docs are the source of truth.\n\n- **Monorepo layout, build, test**: [docs/contributing/monorepo.md](docs/contributing/monorepo.md) and [docs/contributing/architecture.md](docs/contributing/architecture.md)\n- **Verifying fixes at runtime** (real desktop dev build + simulator app, fully scriptable e2e — use this to verify PRs, not just unit tests): [docs/contributing/runtime-verification.md](docs/contributing/runtime-verification.md)\n- **CI and trusting fork PRs**: [docs/contributing/ci.md](docs/contributing/ci.md)\n- **Releasing**: [docs/contributing/releasing.md](docs/contributing/releasing.md)\n","CLAUDE.md":"AGENTS.md","docs/plugins/_category_.json":"{\n  \"label\": \"Plugins\",\n  \"position\": 5,\n  \"link\": {\n    \"type\": \"doc\",\n    \"id\": \"index\"\n  }\n}\n","docs/plugins/apisauce.md":"---\ntitle: Apisauce\n---\n\n# reactotron-apisauce\n\n[Apisauce](http://github.com/skellock/apisauce) is a lightweight wrapper around the fantastic [Axios](https://github.com/mzabriskie/axios) networking library.\n\n![API Response](./images/apisauce/apisauce.jpg)\n\n## Installing\n\n```\nnpm i --save-dev reactotron-apisauce\n```\n\n```\nyarn add reactotron-apisauce -D\n```\n\n## Configuration\n\nTo use the apisauce plugin, add the additional plugin on the `import` line.\n\n```js\nimport apisaucePlugin from \"reactotron-apisauce\" //\n```\n\nThen plug it in when you configure Reactotron.\n\n```js\nReactotron.configure()\n  .use(\n    apisaucePlugin({\n      // ignoreContentTypes: /^(image)\\/.\\*$/i // <--- a way to skip printing the body of some requests (default is any image)\n    })\n  ) // <-- here we go!!!\n  .connect()\n```\n\n## Usage\n\nNext, wherever you create api functions for you application, bring in Reactotron and attach the monitor to your apisauce instance. Apisauce has a feature where you can attach a handler to watch.\n\n1. To view all requests/response flowing through your api\n\n```js\n//for web\nimport Reactotron from \"reactotron-react-js\"\n\n//for mobile\nimport Reactotron from \"reactotron-react-native\"\n\napi.addMonitor(Reactotron.apisauce)\n```\n\n2. To view only the requests/response that have a problem\n\n```js\n//if you just wanted to track on 500's\napi.addMonitor((response) => {\n  if (response.problem === \"SERVER_ERROR\") Reactotron.apisauce(response)\n})\n```\n","docs/plugins/async-storage.md":"---\ntitle: AsyncStorage\n---\n\n# AsyncStorage\n\nThe `asyncStorage` plugin is part of `reactotron-react-native` which allows you to track [AsyncStorage](https://facebook.github.io/react-native/docs/asyncstorage.html) on React Native.\n\n## Usage\n\nTo use the Async Storage plugin, add the additional plugin on the `import` line.\n\n```js\nimport Reactotron, { asyncStorage } from \"reactotron-react-native\"\n```\n\nNext, add it as a plugin to Reactotron.\n\n```js\nReactotron.configure()\n  .use(asyncStorage()) // <--- here we go!\n  .connect()\n```\n\nAnd you're done! Now you can see your AsyncStorage in Reactotron.\n\n## Advanced Usage\n\n`asyncStorage()` also accepts an object with an `ignore` key. The value is an array of strings you would like to prevent sending to Reactotron.\n\n```js\nasyncStorage({\n  ignore: [\"secret\"],\n})\n```\n","docs/plugins/benchmark.md":"---\ntitle: Benchmarking\n---\n\n# Benchmarking\n\nIf you have a function that you believe is slow, you can benchmark it to find any bottlenecks like this:\n\n## Usage\n\n```js\nimport Reactotron from \"reactotron-react-native\"\n\nfunction slowFunction() {\n  const bench = Reactotron.benchmark(\"slow function benchmark\")\n\n  // Code that does thing A\n  bench.step(\"Thing A\")\n\n  // Code that does thing B\n  bench.step(\"Thing B\")\n\n  // Code that does thing C\n  bench.stop(\"Thing C\")\n}\n```\n\nNote that the last call is to `stop` instead of `step`! This is important, otherwise the benchmark results won't display in Reactotron.\n\nWhen `slowFunction` is invoked, Reactotron should show something like this:\n\n![Benchmarking Output](./images/benchmarking/benchmark-output.png)\n\nTips: Time bar `Thing B` is the code execution time from `bench.step(\"Thing A\")` to `bench.step(\"Thing B\")`.\n","docs/plugins/index.md":"# Reactotron Plugins\n\nReactotron has a powerful plugin system with ready-made plugins for you to use in your app. You can also create your own plugins to extend Reactotron's functionality.\n\n## What is a plugin?\n\nA plugin is a way to extend Reactotron's functionality. Plugins can be used to add new features to Reactotron or to add support for new libraries.\n\n## Installing a plugin\n\nTo install a plugin, you need to add it to your Reactotron client. You can do this by importing the plugin and passing it to the `use` method when you configure Reactotron.\n\n```js\nimport Reactotron from \"reactotron-react-native\"\n\nReactotron.configure()\n  .use(somePlugin())\n  .connect()\n```\n\n# Community-made plugins\n\nAnyone can make a Reactotron plugin! If you've made a plugin, please open a PR to add it to this list.\n\n> Note that Infinite Red does not maintain these plugins and can not guarantee compatibility or effectiveness. If you have questions about a plugin, please contact the plugin author.\n\n- [reactotron-react-query](https://github.com/hsndmr/reactotron-react-query)\n","docs/plugins/mst.md":"---\ntitle: Mobx State Tree\n---\n\n# reactotron-mst\n\nBehold! A plugin for Reactotron for working with mobx-state-tree.\n\n# Prerequisites\n\nThis is a plugin for Reactotron, so you'll need either `reactotron-react-native` or `reactotron-react-dom` installed first.\n\nThis is also a plugin for `mobx-state-tree`, so you'll also need that installed as well.\n\n# Installation\n\nAdd a dev-dependency to `reactotron-mst` to your app.\n\n```sh\nyarn add reactotron-mst --dev\n```\n\nor\n\n```sh\nnpm i reactotron-mst --save-dev\n```\n\n# Configuration\n\nTo make Reactotron aware of this plugin, go to the file you are configuring Reactotron currently and add this:\n\n```ts\n// import the plugin\nimport { mst } from \"reactotron-mst\"\n\n// tell Reactotron to use this plugin\nReactotron.use(mst())\n```\n\nThis will bestow Reactotron the power to track `mobx-state-tree` nodes.\n\n# Usage\n\nFinally, you need to give `reactotron-mst` your root tree node.\n\nHere's an example of that in action:\n\n```ts\n// bring in Reactotron\nimport * as Reactotron from \"reactotron-react-native\"\n\n// bring in your mst model\nimport { MyModel } from \"./my-model\"\n\n// create an instance of your model\nconst myTree = MyModel.create()\n\n// let reactotron-mst know about it\nReactotron.trackMstNode(myTree)\n```\n\n# Options\n\nWhen you `use()` the `reactotron-mst`, you can also pass options.\n\n### filter\n\nThe `filter` property provides a way to control what is sent to Reactotron. It is a function which takes an [`IMiddlewareEvent`](https://github.com/mobxjs/mobx-state-tree/blob/master/docs/middleware.md#call-attributes) and returns a `boolean`. If you return `true`, the message will be sent to the Reactotron app. `false` will ignore this message.\n\nHere's an example which will stop all `postProcessSnapshot`-based actions from jumping the wire.\n\n```ts\nimport Tron from \"reactotron-react-native\"\nimport { mst } from \"reactotron-mst\"\n\nconst RX = /postProcessSnapshot/\nconst filter = (event) => RX.test(event.name) === false\n\nTron.use(mst({ filter }))\n```\n\nThe default value for `filter` if you don't provide it is `() => true`, which means everything gets passed to Reactotron.\n\n### queryMode\n\nThe `queryMode` property provides a way to switch between subscribing to live state or snapshots. The only time you'll want to subscribe to snapshots instead of live state is when verifying transitory state (via `postProcessSnapshot`) is not persisted.\n\n# Troubleshooting\n\nThe `trackMstNode()` function will only be available after you setup the `reactotron-mst` plugin. Make sure you do the previous setup step first or you'll see an error that says, `trackMstNode is not a function`.\n\n# Caveats\n\n### Phase 1\n\nThis plugin hooks into Reactotron just like the `redux` one. So the basics are in place, but it'll be a much nicer experience once we start introducing some custom views specifically for `mobx-state-tree`. Consider this plugin's status: phase 1 right now. 😅\n\n### Single Tree\n\nUnlike `redux`, `mobx-state-tree` doesn't have to have a single root node. I personally find it easier to set it up like this, though.\n\nCurrently, `reactotron-mst` only supports tracking 1 tree. Multi-tree support is planned, but requires a bit of retooling of the Reactotron app to support multiple states.\n\nAs a short-term hack, we might be able to find a way to do this by using a prefix to identify which tree you intend to work with. For example, if you would like to subscribe to a piece of state in a different tree, perhaps we could do something like this: `$tree3.currentUser.password`.\n\n### `flow()`-based actions issues\n\nSynchronous actions work well, however there's some issues with async actions when using the `mobx-state-tree` `flow()` function. We're going to have to introduce a new UI view similar to `redux-saga` to display this. In the meantime, the action which kicks off the flow will be logged immediate and the return value will be untracked (for now... sorry!).\n","docs/plugins/networking.md":"---\ntitle: Networking\n---\n\n# Networking\n\nThe `networking` plugin is `reactotron-react-native` which allows you to track all XMLHttpRequests in React Native.\n\nOn Expo SDK 56+, `expo/fetch` is installed as the default `globalThis.fetch`. It is backed by a native module and bypasses `XMLHttpRequest`, so it would otherwise be invisible to this plugin. The plugin detects this case and tracks `expo/fetch` requests as well (see `ignoreExpoFetch` below to opt out). Requests made through React Native's regular XHR-backed `fetch` continue to be tracked via `XMLHttpRequest`, so there is no double-reporting.\n\n## Usage\n\nTo use the `networking` plugin, you need to add the additional plugin on the `import` line.\n\n```js\nimport Reactotron, { networking } from \"reactotron-react-native\";\n```\n\nNext, add it as a plugin to Reactotron.\n\n```js\nReactotron.configure()\n  .use(networking()) // <--- here we go!\n  .connect();\n```\n\nAnd you're done! Now you can see your XMLHttpRequests in Reactotron.\n\n## Advanced Usage\n\n`networking()` also accepts an object with these options:\n\n- `ignoreContentTypes`: a regular expression which, when matched against the `Content-Type` response header, will prevent the data from being displayed in Reactotron. You typically want to do this for images (which is the default). `text/event-stream` response bodies are always skipped so streaming responses are not buffered.\n- `ignoreUrls`: a regular expression which, when matched against the URL of the request, will prevent the request from being tracked in Reactotron. Can be useful for ignoring noisy logging requests.\n- `ignoreExpoFetch`: set to `true` to skip instrumenting Expo's `expo/fetch` (the default `globalThis.fetch` on Expo SDK 56+). Has no effect on non-Expo runtimes, where the global fetch is XHR-backed and already covered by XHR tracking.\n- `fetch`: explicitly pass the fetch function to track; it is wrapped and installed as `globalThis.fetch` on connect, skipping the automatic `expo/fetch` detection. Takes precedence over `ignoreExpoFetch`.\n\n```js\nnetworking({\n  ignoreContentTypes: /^(image)\\/.*$/i,\n  ignoreUrls: /\\/(logs|symbolicate)$/,\n});\n```\n\n### Tracking fetch in expo-router apps\n\nExpo Router (the default `create-expo-app` template) re-wraps `globalThis.fetch` at startup with its `window.location` polyfill, which drops the marker the automatic `expo/fetch` detection looks for — so on expo-router apps fetch requests are silently not tracked. Automatic detection for expo-router apps is in the works (tracked in [#1612](https://github.com/infinitered/reactotron/issues/1612)); until then, use the `fetch` option to track them:\n\n```js\nReactotron.configure()\n  .useReactNative({\n    networking: { fetch: globalThis.fetch },\n  })\n  .connect();\n```\n\nTwo caveats:\n\n- **Ordering**: capture `globalThis.fetch` in code that runs _after_ `expo-router/entry` has set up its wrapper (any module imported from your app code qualifies — the router entry runs first). Capturing it too early passes the pre-router fetch, and the router's wrapper will be bypassed or clobbered.\n- **No XHR-backed fetch**: only pass a fetch that does _not_ go through `XMLHttpRequest` (e.g. don't use this with `EXPO_PUBLIC_USE_RN_FETCH=1`). XHR-backed fetch is already tracked by the XHR interceptor, so wrapping it here would double-report every request.\n\nAlso note: Expo SDK 56 releases before 56.0.19 have a `Response.clone()` bug ([expo#46397](https://github.com/expo/expo/pull/46397)) where cloning a response twice can throw a spurious \"body already used\" error. Reactotron reads response bodies off a clone while tracking is active, so if your app also clones responses, upgrade to expo 56.0.19+ (or SDK 57).\n","docs/plugins/open-in-editor.md":"---\ntitle: Open In Editor\n---\n\n# Open In Editor\n\nThe `openInEditor` plugin allows you to click on a line of code in Reactotron and have it open in your editor.\n\n## Configuration\n\nTo use the `openInEditor` plugin, add the additional plugin on the `import` line.\n\n```js\nimport Reactotron, { openInEditor } from \"reactotron-react-native\"\n```\n\nNext, add it as a plugin to Reactotron.\n\n```js\nReactotron.configure()\n  .use(openInEditor()) // <--- here we go!\n  .connect()\n```\n","docs/plugins/overlay.md":"---\ntitle: Image Overlay\n---\n\n# Overlay\n\nOverlay is a plugin for `reactotron-react-native` which allows you to have an image uploaded\nto your simulator to stay on top of your app. This helps you to create those pixel perfect screens by helping you compare your app to the design.\n\n## Configuration\n\nTo use the Overlay plugin, add the additional plugin on the `import` line.\n\n```js\nimport Reactotron, { overlay } from \"reactotron-react-native\"\n```\n\nAdd it as a plugin to Reactotron.\n\n```js\nReactotron.configure()\n  .useReactNative({\n    overlay: true, // <--- here we go!\n  })\n  .connect()\n```\n\n## Usage\n\nNext, find the root UI component in your app and wrap it.\n\n```js\n// let's pretend this is your app.\nclass MyApp extends Component {\n  render() {\n    return <Text>I may have shipped too early.</Text>\n  }\n}\n\n// let's wrap it, so the overlay stays on top!\nconst MyAppWithBenefits = Reactotron.overlay(MyApp)\n\nexport default MyAppWithBenefits\n```\n\n# React Native Production Caveat\n\nOne common gotcha here is when you make production builds since `reactotron-react-native` is likely setup in `devDependencies`.\n\nIf you'd like to keep it like this (I recommend it!), then perhaps your code might look more like this:\n\n```js\nconst MyAppWithBenefits = __DEV__ ? Reactotron.overlay(MyApp) : MyApp\n```\n\nAnother option is to ship it in your `dependencies` (not recommended -- but sometimes you wanna run it on a device).\n","docs/plugins/react-native-mmkv.md":"---\ntitle: MMKV\n---\n\n# React Native MMKV\n\nThe `reactotron-react-native-mmkv` plugin allows you to track [MMKV](https://github.com/mrousavy/react-native-mmkv) updates.\n\n## Installing\n\n```\nnpm i --save-dev reactotron-react-native-mmkv\n```\n\n```\nyarn add -D reactotron-react-native-mmkv\n```\n\n## Usage\n\nImport your MMKV storage instance:\n\n```tsx\nimport { MMKV } from \"react-native-mmkv\"\nexport const storage = new MMKV()\n```\n\nTo use the `mmkvPlugin`, add the additional plugin on the `import` line.\n\n```tsx\nimport Reactotron from \"reactotron-react-native\"\n// success-line\nimport type { ReactotronReactNative } from \"reactotron-react-native\"\n// success-line\nimport mmkvPlugin from \"reactotron-react-native-mmkv\"\n// success-line\nimport { storage } from \"./mmkv/storage/instance/location\" // <--- update this to your mmkv instance.\n...\nReactotron.configure()\n  // success-line\n  .use(mmkvPlugin<ReactotronReactNative>({ storage }))\n  .connect()\n```\n\nAnd you're done! Now you can see your MMKV in Reactotron.\n\n## Advanced Usage\n\n`mmkvPlugin()` accepts an object with an `ignore` key. The value is an array of strings you would like to prevent sending to Reactotron.\n\n```tsx\nmmkvPlugin<ReactotronReactNative>({\n  storage,\n  ignore: [\"secret\"],\n})\n```\n","docs/plugins/redux.md":"---\ntitle: Redux\n---\n\n# reactotron-redux\n\n[Redux](http://redux.js.org/) is a library for managing global state. It's pretty damn awesome.\n\nAnd everyone knows when you combine two great things, you get a super-awesome thing. Except for toothpaste & orange juice. Keep those separate.\n\n![Keys and Values](./images/redux/redux-keys-values.jpg)\n\n![Dispatching](./images/redux/dispatching.jpg)\n\n![Subscriptions](./images/redux/subscriptions.jpg)\n\n# Installing\n\n`reactotron-redux` is a plugin which lives in a different npm package. You can install it by typing:\n\n```\nnpm install --save-dev reactotron-redux\n```\n\nor\n\n```\nyarn add reactotron-redux -D\n```\n\n# Features\n\n- track when actions are dispatched including timing\n- subscribe to changes within the state tree\n- pull values out on demand\n- view list of keys\n- dispatch actions from Reactotron\n- replay actions\n- hot swap your app state on the fly\n\n# Configuring\n\nTwo files need to change to hookup Reactotron to Redux. First, in your\nReactotronConfig, you'll need to add `reactotron-redux` as plugin\n\n```diff\n// ReactotronConfig.js\n+ import { reactotronRedux } from 'reactotron-redux'\n\n\n// then add it to the plugin list\n- Reactotron\n+ const reactotron = Reactotron\n  .configure({ name: 'React Native Demo' })\n+ .use(reactotronRedux()) //  <- here i am!\n  .connect() //Don't forget about me!\n\n+ export default reactotron // also: export me so I can be referenced by Redux store\n```\n\nNext, we'll add the enhancer from `Reactotron.createEnhancer()` in development.\n\n## Using Redux-Toolkit configureStore\n\nUsing [Redux-Toolkit's `configureStore`](https://redux-toolkit.js.org/api/configureStore), add the `enhancers` key.\n\n```tsx\nconst createEnhancers = (getDefaultEnhancers: GetDefaultEnhancers<any>) => {\n  if (__DEV__) {\n    const reactotron = require(\"../devtools/ReactotronConfig\").default\n    return getDefaultEnhancers().concat(reactotron.createEnhancer())\n  } else {\n    return getDefaultEnhancers()\n  }\n}\n\nexport const store = configureStore({\n  reducer: persistedReducer,\n  enhancers: createEnhancers,\n})\n```\n\n## Using Redux createStore\n\n```diff\nimport { createStore } from 'redux'\n+ import Reactotron from './ReactotronConfig'\n\n- const store = createStore(rootReducer)\n+ const store = createStore(rootReducer, Reactotron.createEnhancer())\n+ // or const store = createStore(rootReducer, preloadedState, Reactotron.createEnhancer())\n```\n\nNote: passing enhancer as last argument requires redux@>=3.1.0\n\n### If you have middleware\n\n```diff\nimport { createStore, compose } from 'redux'\n+ import Reactotron from './ReactotronConfig'\n\n- const store = createStore(rootReducer, compose(middleware))\n+ const store = createStore(rootReducer, compose(middleware, Reactotron.createEnhancer()))\n```\n\n# Options\n\n`reactotronRedux()` accepts an optional parameter which is an object you can use\nto configure\n\n#### except\n\n`except` is an array of strings that match actions flowing through Redux.\n\nIf you have some actions you'd rather just not see (for example, `redux-saga`\ntriggers a little bit of noise), you can suppress them:\n\n```js\nreactotronRedux({\n  except: [\"EFFECT_TRIGGERED\", \"EFFECT_RESOLVED\", \"EFFECT_REJECTED\"],\n})\n```\n\n#### isActionImportant\n\n`isActionImportant` is a function which receives an action and returns a boolean.\n`true` will cause the action to show up in the Reactotron app with a highlight.\n\n```js\nreactotronRedux({\n  isActionImportant: (action) => action.type === \"repo.receive\",\n})\n```\n\n#### onBackup\n\n`onBackup` fires when we're about to transfer a copy of your Redux global state\ntree and send it to the server. It accepts an object called `state` and returns\nan object called `state`.\n\nYou can use this to prevent big, sensitive, or transient data from going to\nReactotron.\n\n#### onRestore\n\n`onRestore` is the opposite of `onBackup`. It will fire when the Reactotron app\nsends a new copy of state to the app.\n","docs/plugins/storybook.md":"---\ntitle: Storybook\n---\n\n# Storybook (React Native Only)\n\n`reactotron-react-native` ships with a plugin called `storybook`.\n\nThis plugin, when configured properly, allows the switching to Storybook from the Reactotron interface\n\n## Configuration\n\nThe storybook plugin gets turned on automatically when you use `.useReactNative()`. If you don't want to use that method you can manually wire up the plugin:\n\nWherever you setup your Reactotron in your app, you also add the additional plugin on the `import` line.\n\n```js\nimport Reactotron, { storybook } from \"reactotron-react-native\"\n```\n\nNext, add it as a plugin to Reactotron.\n\n```js\nReactotron.configure()\n  .useReactNative({\n    storybook: true,\n  })\n  .connect()\n```\n\n## Usage\n\nOnce the plugin is configured you have to wrap your entire app in a HOC provided by Reactotron. In addition to that Storybook requires some configuration. Here is an example:\n\n```js\nimport { getStorybookUI, configure } from '@storybook/react-native'\n\n{...}\n\nconfigure(() => {\n  require('../../storybook/stories') // This should point to your \"root\" set of stories\n}, module)\n\nconst StorybookUIRoot = getStorybookUI({ port: 7007, onDeviceUI: true }) // This configuration can be changed based upon personal wants\n\nclass StorybookUIHMRRoot extends Component {\n  render () {\n    return <StorybookUIRoot />\n  }\n}\n\n// Assuming you have Reactotron on console.tron\nexport default console.tron.storybookSwitcher(StorybookUIHMRRoot)(RootContainer)\n```\n","docs/plugins/track-global-errors.md":"---\ntitle: Global Errors\n---\n\n# Track Global Errors\n\nThe goal of this plugin is to ensure all errors are thrown over to Reactotron for display.\nBoth `reactotron-react-native` and `reactotron-react-js` ship with this plugin.\n\n# Usage\n\nTo use the `trackGlobalErrors` plugin, add the additional plugin on the `import` line.\n\n```js\nimport Reactotron, { trackGlobalErrors } from \"reactotron-react-native\";\n```\n\nor\n\n```js\nimport Reactotron, { trackGlobalErrors } from \"reactotron-react-js\";\n```\n\nNext, add it as a plugin to Reactotron.\n\n```js\nReactotron.configure()\n  .use(trackGlobalErrors()) // <--- here we go!\n  .connect();\n```\n\n## Options\n\nVeto is function that allows you to select frames you would like to leave out of the stack trace.\n\nFor example, on React Native, you'd like to leave off any frames sourced from React Native itself\n\n```js\nReactotron.configure()\n  .use(\n    trackGlobalErrors({\n      veto: (frame) =>\n        !frame.fileName.includes(\"/node_modules/react-native/\"),\n    })\n  )\n  .connect();\n```\n\n`veto` is a function that takes an `object` and returns a `boolean`. `true` = keep it. `false` = ditch it.\n\n> **Note:** This behavior is inverted from the originally intended design. The function was meant to work as \"true = ditch it, false = keep it\", but due to a long-standing implementation detail (using `.filter()` which keeps truthy values), the actual behavior has been the opposite. Since this behavior has existed for so long, it's now considered the expected behavior and the documentation has been updated to reflect it.\n\nThe frame object passed into `veto` has these properties.\n\n```\nfunctionName:  the name of the function or null if an anonymous function\nlineNumber:    the line number of the error\ncolumnNumber:  the column number of the error\nfileName:      the name of the file\n```\n\n### React JS Source Maps\n\nSource maps for projects in webpack need to have the `devtool` set to `source-map`. On `create-react-app` based apps, they use `eval`, so this will not work. You will have to switch your development webpack configuration to support this.\n\nYou also have the option to not do source map lookups, but still pass errors along by going into offline mode.\n\n```js\nReactotron.configure()\n  .use(\n    trackGlobalErrors({\n      offline: true,\n    })\n  )\n  .connect();\n```\n\n# How It Works Internally\n\n### React JS\n\nIt hijacks the browser's `window.onerror` event, immediately calling the previous `onerror`, then attempts to resolve the source of the errors via a source-map lookup. If successful, it throws a message over to Reactotron.\n\n### React Native\n\nOn React Native, it hooks `NativeModules.LogBox.addException`. This is an internal method that React Native uses internally to report errors to LogBox. It always calls the original function so it won't break anything internally. Your Red Box will still show.\n\nBecause we're hooking a Facebook internal function, this is a bit fragile. We'll stay on top of any React Native upgrades that might cause problems should the API change.\n","docs/plugins/track-global-logs.md":"---\ntitle: Global Logs\n---\n\n# Documentation: `trackGlobalLogs`\n\n## Overview\n\nThe `trackGlobalLogs` plugin for Reactotron is designed to intercept calls made to `console.log`, `console.warn`, and `console.debug`, forwarding them to the Reactotron logger. This functionality is useful for monitoring and analyzing logs within your React Native or React JS applications.\n\n### Usage\n\nTo integrate the `trackGlobalLogs` plugin into your Reactotron setup, follow these steps:\n\n#### Default\n\nThis plugin is included by default in Reactotron when using `reactotron-react-native`.\n\n```js\nimport Reactotron from \"reactotron-react-native\";\n\nReactotron.configure()\n  .useReactNative() // included by default\n  .connect();\n```\n\n#### Custom\n\nIf are not using the `useReactNative` method for some reason, you can add the plugin manually like so:\n\n1. Import the `trackGlobalLogs` function from the Reactotron package:\n\n```js\nimport Reactotron, { trackGlobalLogs } from \"reactotron-react-native\";\n```\n\n2. Add the `trackGlobalLogs` plugin to your Reactotron configuration as shown below:\n\n```js\nReactotron.configure().use(trackGlobalLogs()).connect();\n```\n\nThis enables the plugin to start intercepting global logs once your Reactotron instance is connected.\n\n### How It Works Internally\n\nFor both React Native and React JS applications, the `trackGlobalLogs` plugin works by monkey-patching references to `console.log`, `console.warn`, and `console.debug`. It intercepts calls to these console methods and, in addition to the original console output, also sends the log data to the Reactotron logger for further analysis.\n\nPlease note that there are no additional configuration options provided by the plugin itself. It automatically captures `console.log`, `console.warn`, and `console.debug` calls without any customization options.\n"},"files":{"AGENTS.md":"# Agent Notes\n\nMinimal router — the linked docs are the source of truth.\n\n- **Monorepo layout, build, test**: [docs/contributing/monorepo.md](docs/contributing/monorepo.md) and [docs/contributing/architecture.md](docs/contributing/architecture.md)\n- **Verifying fixes at runtime** (real desktop dev build + simulator app, fully scriptable e2e — use this to verify PRs, not just unit tests): [docs/contributing/runtime-verification.md](docs/contributing/runtime-verification.md)\n- **CI and trusting fork PRs**: [docs/contributing/ci.md](docs/contributing/ci.md)\n- **Releasing**: [docs/contributing/releasing.md](docs/contributing/releasing.md)\n","CLAUDE.md":"AGENTS.md","docs/plugins/_category_.json":"{\n  \"label\": \"Plugins\",\n  \"position\": 5,\n  \"link\": {\n    \"type\": \"doc\",\n    \"id\": \"index\"\n  }\n}\n","docs/plugins/apisauce.md":"---\ntitle: Apisauce\n---\n\n# reactotron-apisauce\n\n[Apisauce](http://github.com/skellock/apisauce) is a lightweight wrapper around the fantastic [Axios](https://github.com/mzabriskie/axios) networking library.\n\n![API Response](./images/apisauce/apisauce.jpg)\n\n## Installing\n\n```\nnpm i --save-dev reactotron-apisauce\n```\n\n```\nyarn add reactotron-apisauce -D\n```\n\n## Configuration\n\nTo use the apisauce plugin, add the additional plugin on the `import` line.\n\n```js\nimport apisaucePlugin from \"reactotron-apisauce\" //\n```\n\nThen plug it in when you configure Reactotron.\n\n```js\nReactotron.configure()\n  .use(\n    apisaucePlugin({\n      // ignoreContentTypes: /^(image)\\/.\\*$/i // <--- a way to skip printing the body of some requests (default is any image)\n    })\n  ) // <-- here we go!!!\n  .connect()\n```\n\n## Usage\n\nNext, wherever you create api functions for you application, bring in Reactotron and attach the monitor to your apisauce instance. Apisauce has a feature where you can attach a handler to watch.\n\n1. To view all requests/response flowing through your api\n\n```js\n//for web\nimport Reactotron from \"reactotron-react-js\"\n\n//for mobile\nimport Reactotron from \"reactotron-react-native\"\n\napi.addMonitor(Reactotron.apisauce)\n```\n\n2. To view only the requests/response that have a problem\n\n```js\n//if you just wanted to track on 500's\napi.addMonitor((response) => {\n  if (response.problem === \"SERVER_ERROR\") Reactotron.apisauce(response)\n})\n```\n","docs/plugins/async-storage.md":"---\ntitle: AsyncStorage\n---\n\n# AsyncStorage\n\nThe `asyncStorage` plugin is part of `reactotron-react-native` which allows you to track [AsyncStorage](https://facebook.github.io/react-native/docs/asyncstorage.html) on React Native.\n\n## Usage\n\nTo use the Async Storage plugin, add the additional plugin on the `import` line.\n\n```js\nimport Reactotron, { asyncStorage } from \"reactotron-react-native\"\n```\n\nNext, add it as a plugin to Reactotron.\n\n```js\nReactotron.configure()\n  .use(asyncStorage()) // <--- here we go!\n  .connect()\n```\n\nAnd you're done! Now you can see your AsyncStorage in Reactotron.\n\n## Advanced Usage\n\n`asyncStorage()` also accepts an object with an `ignore` key. The value is an array of strings you would like to prevent sending to Reactotron.\n\n```js\nasyncStorage({\n  ignore: [\"secret\"],\n})\n```\n","docs/plugins/benchmark.md":"---\ntitle: Benchmarking\n---\n\n# Benchmarking\n\nIf you have a function that you believe is slow, you can benchmark it to find any bottlenecks like this:\n\n## Usage\n\n```js\nimport Reactotron from \"reactotron-react-native\"\n\nfunction slowFunction() {\n  const bench = Reactotron.benchmark(\"slow function benchmark\")\n\n  // Code that does thing A\n  bench.step(\"Thing A\")\n\n  // Code that does thing B\n  bench.step(\"Thing B\")\n\n  // Code that does thing C\n  bench.stop(\"Thing C\")\n}\n```\n\nNote that the last call is to `stop` instead of `step`! This is important, otherwise the benchmark results won't display in Reactotron.\n\nWhen `slowFunction` is invoked, Reactotron should show something like this:\n\n![Benchmarking Output](./images/benchmarking/benchmark-output.png)\n\nTips: Time bar `Thing B` is the code execution time from `bench.step(\"Thing A\")` to `bench.step(\"Thing B\")`.\n","docs/plugins/index.md":"# Reactotron Plugins\n\nReactotron has a powerful plugin system with ready-made plugins for you to use in your app. You can also create your own plugins to extend Reactotron's functionality.\n\n## What is a plugin?\n\nA plugin is a way to extend Reactotron's functionality. Plugins can be used to add new features to Reactotron or to add support for new libraries.\n\n## Installing a plugin\n\nTo install a plugin, you need to add it to your Reactotron client. You can do this by importing the plugin and passing it to the `use` method when you configure Reactotron.\n\n```js\nimport Reactotron from \"reactotron-react-native\"\n\nReactotron.configure()\n  .use(somePlugin())\n  .connect()\n```\n\n# Community-made plugins\n\nAnyone can make a Reactotron plugin! If you've made a plugin, please open a PR to add it to this list.\n\n> Note that Infinite Red does not maintain these plugins and can not guarantee compatibility or effectiveness. If you have questions about a plugin, please contact the plugin author.\n\n- [reactotron-react-query](https://github.com/hsndmr/reactotron-react-query)\n","docs/plugins/mst.md":"---\ntitle: Mobx State Tree\n---\n\n# reactotron-mst\n\nBehold! A plugin for Reactotron for working with mobx-state-tree.\n\n# Prerequisites\n\nThis is a plugin for Reactotron, so you'll need either `reactotron-react-native` or `reactotron-react-dom` installed first.\n\nThis is also a plugin for `mobx-state-tree`, so you'll also need that installed as well.\n\n# Installation\n\nAdd a dev-dependency to `reactotron-mst` to your app.\n\n```sh\nyarn add reactotron-mst --dev\n```\n\nor\n\n```sh\nnpm i reactotron-mst --save-dev\n```\n\n# Configuration\n\nTo make Reactotron aware of this plugin, go to the file you are configuring Reactotron currently and add this:\n\n```ts\n// import the plugin\nimport { mst } from \"reactotron-mst\"\n\n// tell Reactotron to use this plugin\nReactotron.use(mst())\n```\n\nThis will bestow Reactotron the power to track `mobx-state-tree` nodes.\n\n# Usage\n\nFinally, you need to give `reactotron-mst` your root tree node.\n\nHere's an example of that in action:\n\n```ts\n// bring in Reactotron\nimport * as Reactotron from \"reactotron-react-native\"\n\n// bring in your mst model\nimport { MyModel } from \"./my-model\"\n\n// create an instance of your model\nconst myTree = MyModel.create()\n\n// let reactotron-mst know about it\nReactotron.trackMstNode(myTree)\n```\n\n# Options\n\nWhen you `use()` the `reactotron-mst`, you can also pass options.\n\n### filter\n\nThe `filter` property provides a way to control what is sent to Reactotron. It is a function which takes an [`IMiddlewareEvent`](https://github.com/mobxjs/mobx-state-tree/blob/master/docs/middleware.md#call-attributes) and returns a `boolean`. If you return `true`, the message will be sent to the Reactotron app. `false` will ignore this message.\n\nHere's an example which will stop all `postProcessSnapshot`-based actions from jumping the wire.\n\n```ts\nimport Tron from \"reactotron-react-native\"\nimport { mst } from \"reactotron-mst\"\n\nconst RX = /postProcessSnapshot/\nconst filter = (event) => RX.test(event.name) === false\n\nTron.use(mst({ filter }))\n```\n\nThe default value for `filter` if you don't provide it is `() => true`, which means everything gets passed to Reactotron.\n\n### queryMode\n\nThe `queryMode` property provides a way to switch between subscribing to live state or snapshots. The only time you'll want to subscribe to snapshots instead of live state is when verifying transitory state (via `postProcessSnapshot`) is not persisted.\n\n# Troubleshooting\n\nThe `trackMstNode()` function will only be available after you setup the `reactotron-mst` plugin. Make sure you do the previous setup step first or you'll see an error that says, `trackMstNode is not a function`.\n\n# Caveats\n\n### Phase 1\n\nThis plugin hooks into Reactotron just like the `redux` one. So the basics are in place, but it'll be a much nicer experience once we start introducing some custom views specifically for `mobx-state-tree`. Consider this plugin's status: phase 1 right now. 😅\n\n### Single Tree\n\nUnlike `redux`, `mobx-state-tree` doesn't have to have a single root node. I personally find it easier to set it up like this, though.\n\nCurrently, `reactotron-mst` only supports tracking 1 tree. Multi-tree support is planned, but requires a bit of retooling of the Reactotron app to support multiple states.\n\nAs a short-term hack, we might be able to find a way to do this by using a prefix to identify which tree you intend to work with. For example, if you would like to subscribe to a piece of state in a different tree, perhaps we could do something like this: `$tree3.currentUser.password`.\n\n### `flow()`-based actions issues\n\nSynchronous actions work well, however there's some issues with async actions when using the `mobx-state-tree` `flow()` function. We're going to have to introduce a new UI view similar to `redux-saga` to display this. In the meantime, the action which kicks off the flow will be logged immediate and the return value will be untracked (for now... sorry!).\n","docs/plugins/networking.md":"---\ntitle: Networking\n---\n\n# Networking\n\nThe `networking` plugin is `reactotron-react-native` which allows you to track all XMLHttpRequests in React Native.\n\nOn Expo SDK 56+, `expo/fetch` is installed as the default `globalThis.fetch`. It is backed by a native module and bypasses `XMLHttpRequest`, so it would otherwise be invisible to this plugin. The plugin detects this case and tracks `expo/fetch` requests as well (see `ignoreExpoFetch` below to opt out). Requests made through React Native's regular XHR-backed `fetch` continue to be tracked via `XMLHttpRequest`, so there is no double-reporting.\n\n## Usage\n\nTo use the `networking` plugin, you need to add the additional plugin on the `import` line.\n\n```js\nimport Reactotron, { networking } from \"reactotron-react-native\";\n```\n\nNext, add it as a plugin to Reactotron.\n\n```js\nReactotron.configure()\n  .use(networking()) // <--- here we go!\n  .connect();\n```\n\nAnd you're done! Now you can see your XMLHttpRequests in Reactotron.\n\n## Advanced Usage\n\n`networking()` also accepts an object with these options:\n\n- `ignoreContentTypes`: a regular expression which, when matched against the `Content-Type` response header, will prevent the data from being displayed in Reactotron. You typically want to do this for images (which is the default). `text/event-stream` response bodies are always skipped so streaming responses are not buffered.\n- `ignoreUrls`: a regular expression which, when matched against the URL of the request, will prevent the request from being tracked in Reactotron. Can be useful for ignoring noisy logging requests.\n- `ignoreExpoFetch`: set to `true` to skip instrumenting Expo's `expo/fetch` (the default `globalThis.fetch` on Expo SDK 56+). Has no effect on non-Expo runtimes, where the global fetch is XHR-backed and already covered by XHR tracking.\n- `fetch`: explicitly pass the fetch function to track; it is wrapped and installed as `globalThis.fetch` on connect, skipping the automatic `expo/fetch` detection. Takes precedence over `ignoreExpoFetch`.\n\n```js\nnetworking({\n  ignoreContentTypes: /^(image)\\/.*$/i,\n  ignoreUrls: /\\/(logs|symbolicate)$/,\n});\n```\n\n### Tracking fetch in expo-router apps\n\nExpo Router (the default `create-expo-app` template) re-wraps `globalThis.fetch` at startup with its `window.location` polyfill, which drops the marker the automatic `expo/fetch` detection looks for — so on expo-router apps fetch requests are silently not tracked. Automatic detection for expo-router apps is in the works (tracked in [#1612](https://github.com/infinitered/reactotron/issues/1612)); until then, use the `fetch` option to track them:\n\n```js\nReactotron.configure()\n  .useReactNative({\n    networking: { fetch: globalThis.fetch },\n  })\n  .connect();\n```\n\nTwo caveats:\n\n- **Ordering**: capture `globalThis.fetch` in code that runs _after_ `expo-router/entry` has set up its wrapper (any module imported from your app code qualifies — the router entry runs first). Capturing it too early passes the pre-router fetch, and the router's wrapper will be bypassed or clobbered.\n- **No XHR-backed fetch**: only pass a fetch that does _not_ go through `XMLHttpRequest` (e.g. don't use this with `EXPO_PUBLIC_USE_RN_FETCH=1`). XHR-backed fetch is already tracked by the XHR interceptor, so wrapping it here would double-report every request.\n\nAlso note: Expo SDK 56 releases before 56.0.19 have a `Response.clone()` bug ([expo#46397](https://github.com/expo/expo/pull/46397)) where cloning a response twice can throw a spurious \"body already used\" error. Reactotron reads response bodies off a clone while tracking is active, so if your app also clones responses, upgrade to expo 56.0.19+ (or SDK 57).\n","docs/plugins/open-in-editor.md":"---\ntitle: Open In Editor\n---\n\n# Open In Editor\n\nThe `openInEditor` plugin allows you to click on a line of code in Reactotron and have it open in your editor.\n\n## Configuration\n\nTo use the `openInEditor` plugin, add the additional plugin on the `import` line.\n\n```js\nimport Reactotron, { openInEditor } from \"reactotron-react-native\"\n```\n\nNext, add it as a plugin to Reactotron.\n\n```js\nReactotron.configure()\n  .use(openInEditor()) // <--- here we go!\n  .connect()\n```\n","docs/plugins/overlay.md":"---\ntitle: Image Overlay\n---\n\n# Overlay\n\nOverlay is a plugin for `reactotron-react-native` which allows you to have an image uploaded\nto your simulator to stay on top of your app. This helps you to create those pixel perfect screens by helping you compare your app to the design.\n\n## Configuration\n\nTo use the Overlay plugin, add the additional plugin on the `import` line.\n\n```js\nimport Reactotron, { overlay } from \"reactotron-react-native\"\n```\n\nAdd it as a plugin to Reactotron.\n\n```js\nReactotron.configure()\n  .useReactNative({\n    overlay: true, // <--- here we go!\n  })\n  .connect()\n```\n\n## Usage\n\nNext, find the root UI component in your app and wrap it.\n\n```js\n// let's pretend this is your app.\nclass MyApp extends Component {\n  render() {\n    return <Text>I may have shipped too early.</Text>\n  }\n}\n\n// let's wrap it, so the overlay stays on top!\nconst MyAppWithBenefits = Reactotron.overlay(MyApp)\n\nexport default MyAppWithBenefits\n```\n\n# React Native Production Caveat\n\nOne common gotcha here is when you make production builds since `reactotron-react-native` is likely setup in `devDependencies`.\n\nIf you'd like to keep it like this (I recommend it!), then perhaps your code might look more like this:\n\n```js\nconst MyAppWithBenefits = __DEV__ ? Reactotron.overlay(MyApp) : MyApp\n```\n\nAnother option is to ship it in your `dependencies` (not recommended -- but sometimes you wanna run it on a device).\n","docs/plugins/react-native-mmkv.md":"---\ntitle: MMKV\n---\n\n# React Native MMKV\n\nThe `reactotron-react-native-mmkv` plugin allows you to track [MMKV](https://github.com/mrousavy/react-native-mmkv) updates.\n\n## Installing\n\n```\nnpm i --save-dev reactotron-react-native-mmkv\n```\n\n```\nyarn add -D reactotron-react-native-mmkv\n```\n\n## Usage\n\nImport your MMKV storage instance:\n\n```tsx\nimport { MMKV } from \"react-native-mmkv\"\nexport const storage = new MMKV()\n```\n\nTo use the `mmkvPlugin`, add the additional plugin on the `import` line.\n\n```tsx\nimport Reactotron from \"reactotron-react-native\"\n// success-line\nimport type { ReactotronReactNative } from \"reactotron-react-native\"\n// success-line\nimport mmkvPlugin from \"reactotron-react-native-mmkv\"\n// success-line\nimport { storage } from \"./mmkv/storage/instance/location\" // <--- update this to your mmkv instance.\n...\nReactotron.configure()\n  // success-line\n  .use(mmkvPlugin<ReactotronReactNative>({ storage }))\n  .connect()\n```\n\nAnd you're done! Now you can see your MMKV in Reactotron.\n\n## Advanced Usage\n\n`mmkvPlugin()` accepts an object with an `ignore` key. The value is an array of strings you would like to prevent sending to Reactotron.\n\n```tsx\nmmkvPlugin<ReactotronReactNative>({\n  storage,\n  ignore: [\"secret\"],\n})\n```\n","docs/plugins/redux.md":"---\ntitle: Redux\n---\n\n# reactotron-redux\n\n[Redux](http://redux.js.org/) is a library for managing global state. It's pretty damn awesome.\n\nAnd everyone knows when you combine two great things, you get a super-awesome thing. Except for toothpaste & orange juice. Keep those separate.\n\n![Keys and Values](./images/redux/redux-keys-values.jpg)\n\n![Dispatching](./images/redux/dispatching.jpg)\n\n![Subscriptions](./images/redux/subscriptions.jpg)\n\n# Installing\n\n`reactotron-redux` is a plugin which lives in a different npm package. You can install it by typing:\n\n```\nnpm install --save-dev reactotron-redux\n```\n\nor\n\n```\nyarn add reactotron-redux -D\n```\n\n# Features\n\n- track when actions are dispatched including timing\n- subscribe to changes within the state tree\n- pull values out on demand\n- view list of keys\n- dispatch actions from Reactotron\n- replay actions\n- hot swap your app state on the fly\n\n# Configuring\n\nTwo files need to change to hookup Reactotron to Redux. First, in your\nReactotronConfig, you'll need to add `reactotron-redux` as plugin\n\n```diff\n// ReactotronConfig.js\n+ import { reactotronRedux } from 'reactotron-redux'\n\n\n// then add it to the plugin list\n- Reactotron\n+ const reactotron = Reactotron\n  .configure({ name: 'React Native Demo' })\n+ .use(reactotronRedux()) //  <- here i am!\n  .connect() //Don't forget about me!\n\n+ export default reactotron // also: export me so I can be referenced by Redux store\n```\n\nNext, we'll add the enhancer from `Reactotron.createEnhancer()` in development.\n\n## Using Redux-Toolkit configureStore\n\nUsing [Redux-Toolkit's `configureStore`](https://redux-toolkit.js.org/api/configureStore), add the `enhancers` key.\n\n```tsx\nconst createEnhancers = (getDefaultEnhancers: GetDefaultEnhancers<any>) => {\n  if (__DEV__) {\n    const reactotron = require(\"../devtools/ReactotronConfig\").default\n    return getDefaultEnhancers().concat(reactotron.createEnhancer())\n  } else {\n    return getDefaultEnhancers()\n  }\n}\n\nexport const store = configureStore({\n  reducer: persistedReducer,\n  enhancers: createEnhancers,\n})\n```\n\n## Using Redux createStore\n\n```diff\nimport { createStore } from 'redux'\n+ import Reactotron from './ReactotronConfig'\n\n- const store = createStore(rootReducer)\n+ const store = createStore(rootReducer, Reactotron.createEnhancer())\n+ // or const store = createStore(rootReducer, preloadedState, Reactotron.createEnhancer())\n```\n\nNote: passing enhancer as last argument requires redux@>=3.1.0\n\n### If you have middleware\n\n```diff\nimport { createStore, compose } from 'redux'\n+ import Reactotron from './ReactotronConfig'\n\n- const store = createStore(rootReducer, compose(middleware))\n+ const store = createStore(rootReducer, compose(middleware, Reactotron.createEnhancer()))\n```\n\n# Options\n\n`reactotronRedux()` accepts an optional parameter which is an object you can use\nto configure\n\n#### except\n\n`except` is an array of strings that match actions flowing through Redux.\n\nIf you have some actions you'd rather just not see (for example, `redux-saga`\ntriggers a little bit of noise), you can suppress them:\n\n```js\nreactotronRedux({\n  except: [\"EFFECT_TRIGGERED\", \"EFFECT_RESOLVED\", \"EFFECT_REJECTED\"],\n})\n```\n\n#### isActionImportant\n\n`isActionImportant` is a function which receives an action and returns a boolean.\n`true` will cause the action to show up in the Reactotron app with a highlight.\n\n```js\nreactotronRedux({\n  isActionImportant: (action) => action.type === \"repo.receive\",\n})\n```\n\n#### onBackup\n\n`onBackup` fires when we're about to transfer a copy of your Redux global state\ntree and send it to the server. It accepts an object called `state` and returns\nan object called `state`.\n\nYou can use this to prevent big, sensitive, or transient data from going to\nReactotron.\n\n#### onRestore\n\n`onRestore` is the opposite of `onBackup`. It will fire when the Reactotron app\nsends a new copy of state to the app.\n","docs/plugins/storybook.md":"---\ntitle: Storybook\n---\n\n# Storybook (React Native Only)\n\n`reactotron-react-native` ships with a plugin called `storybook`.\n\nThis plugin, when configured properly, allows the switching to Storybook from the Reactotron interface\n\n## Configuration\n\nThe storybook plugin gets turned on automatically when you use `.useReactNative()`. If you don't want to use that method you can manually wire up the plugin:\n\nWherever you setup your Reactotron in your app, you also add the additional plugin on the `import` line.\n\n```js\nimport Reactotron, { storybook } from \"reactotron-react-native\"\n```\n\nNext, add it as a plugin to Reactotron.\n\n```js\nReactotron.configure()\n  .useReactNative({\n    storybook: true,\n  })\n  .connect()\n```\n\n## Usage\n\nOnce the plugin is configured you have to wrap your entire app in a HOC provided by Reactotron. In addition to that Storybook requires some configuration. Here is an example:\n\n```js\nimport { getStorybookUI, configure } from '@storybook/react-native'\n\n{...}\n\nconfigure(() => {\n  require('../../storybook/stories') // This should point to your \"root\" set of stories\n}, module)\n\nconst StorybookUIRoot = getStorybookUI({ port: 7007, onDeviceUI: true }) // This configuration can be changed based upon personal wants\n\nclass StorybookUIHMRRoot extends Component {\n  render () {\n    return <StorybookUIRoot />\n  }\n}\n\n// Assuming you have Reactotron on console.tron\nexport default console.tron.storybookSwitcher(StorybookUIHMRRoot)(RootContainer)\n```\n","docs/plugins/track-global-errors.md":"---\ntitle: Global Errors\n---\n\n# Track Global Errors\n\nThe goal of this plugin is to ensure all errors are thrown over to Reactotron for display.\nBoth `reactotron-react-native` and `reactotron-react-js` ship with this plugin.\n\n# Usage\n\nTo use the `trackGlobalErrors` plugin, add the additional plugin on the `import` line.\n\n```js\nimport Reactotron, { trackGlobalErrors } from \"reactotron-react-native\";\n```\n\nor\n\n```js\nimport Reactotron, { trackGlobalErrors } from \"reactotron-react-js\";\n```\n\nNext, add it as a plugin to Reactotron.\n\n```js\nReactotron.configure()\n  .use(trackGlobalErrors()) // <--- here we go!\n  .connect();\n```\n\n## Options\n\nVeto is function that allows you to select frames you would like to leave out of the stack trace.\n\nFor example, on React Native, you'd like to leave off any frames sourced from React Native itself\n\n```js\nReactotron.configure()\n  .use(\n    trackGlobalErrors({\n      veto: (frame) =>\n        !frame.fileName.includes(\"/node_modules/react-native/\"),\n    })\n  )\n  .connect();\n```\n\n`veto` is a function that takes an `object` and returns a `boolean`. `true` = keep it. `false` = ditch it.\n\n> **Note:** This behavior is inverted from the originally intended design. The function was meant to work as \"true = ditch it, false = keep it\", but due to a long-standing implementation detail (using `.filter()` which keeps truthy values), the actual behavior has been the opposite. Since this behavior has existed for so long, it's now considered the expected behavior and the documentation has been updated to reflect it.\n\nThe frame object passed into `veto` has these properties.\n\n```\nfunctionName:  the name of the function or null if an anonymous function\nlineNumber:    the line number of the error\ncolumnNumber:  the column number of the error\nfileName:      the name of the file\n```\n\n### React JS Source Maps\n\nSource maps for projects in webpack need to have the `devtool` set to `source-map`. On `create-react-app` based apps, they use `eval`, so this will not work. You will have to switch your development webpack configuration to support this.\n\nYou also have the option to not do source map lookups, but still pass errors along by going into offline mode.\n\n```js\nReactotron.configure()\n  .use(\n    trackGlobalErrors({\n      offline: true,\n    })\n  )\n  .connect();\n```\n\n# How It Works Internally\n\n### React JS\n\nIt hijacks the browser's `window.onerror` event, immediately calling the previous `onerror`, then attempts to resolve the source of the errors via a source-map lookup. If successful, it throws a message over to Reactotron.\n\n### React Native\n\nOn React Native, it hooks `NativeModules.LogBox.addException`. This is an internal method that React Native uses internally to report errors to LogBox. It always calls the original function so it won't break anything internally. Your Red Box will still show.\n\nBecause we're hooking a Facebook internal function, this is a bit fragile. We'll stay on top of any React Native upgrades that might cause problems should the API change.\n","docs/plugins/track-global-logs.md":"---\ntitle: Global Logs\n---\n\n# Documentation: `trackGlobalLogs`\n\n## Overview\n\nThe `trackGlobalLogs` plugin for Reactotron is designed to intercept calls made to `console.log`, `console.warn`, and `console.debug`, forwarding them to the Reactotron logger. This functionality is useful for monitoring and analyzing logs within your React Native or React JS applications.\n\n### Usage\n\nTo integrate the `trackGlobalLogs` plugin into your Reactotron setup, follow these steps:\n\n#### Default\n\nThis plugin is included by default in Reactotron when using `reactotron-react-native`.\n\n```js\nimport Reactotron from \"reactotron-react-native\";\n\nReactotron.configure()\n  .useReactNative() // included by default\n  .connect();\n```\n\n#### Custom\n\nIf are not using the `useReactNative` method for some reason, you can add the plugin manually like so:\n\n1. Import the `trackGlobalLogs` function from the Reactotron package:\n\n```js\nimport Reactotron, { trackGlobalLogs } from \"reactotron-react-native\";\n```\n\n2. Add the `trackGlobalLogs` plugin to your Reactotron configuration as shown below:\n\n```js\nReactotron.configure().use(trackGlobalLogs()).connect();\n```\n\nThis enables the plugin to start intercepting global logs once your Reactotron instance is connected.\n\n### How It Works Internally\n\nFor both React Native and React JS applications, the `trackGlobalLogs` plugin works by monkey-patching references to `console.log`, `console.warn`, and `console.debug`. It intercepts calls to these console methods and, in addition to the original console output, also sends the log data to the Reactotron logger for further analysis.\n\nPlease note that there are no additional configuration options provided by the plugin itself. It automatically captures `console.log`, `console.warn`, and `console.debug` calls without any customization options.\n"},"items":[{"name":"AGENTS.md","path":"AGENTS.md","rawUrl":"https://raw.githubusercontent.com/infinitered/reactotron/HEAD/AGENTS.md","title":"AI Agent Protocol & Instructions","category":"root-instruction","format":"markdown","content":"# Agent Notes\n\nMinimal router — the linked docs are the source of truth.\n\n- **Monorepo layout, build, test**: [docs/contributing/monorepo.md](docs/contributing/monorepo.md) and [docs/contributing/architecture.md](docs/contributing/architecture.md)\n- **Verifying fixes at runtime** (real desktop dev build + simulator app, fully scriptable e2e — use this to verify PRs, not just unit tests): [docs/contributing/runtime-verification.md](docs/contributing/runtime-verification.md)\n- **CI and trusting fork PRs**: [docs/contributing/ci.md](docs/contributing/ci.md)\n- **Releasing**: [docs/contributing/releasing.md](docs/contributing/releasing.md)\n","isInternal":false,"tokens":169,"sizeBytes":647},{"name":"CLAUDE.md","path":"CLAUDE.md","rawUrl":"https://raw.githubusercontent.com/infinitered/reactotron/HEAD/CLAUDE.md","title":"Claude Agent Guidelines & System Prompt","category":"claude-rule","format":"markdown","content":"AGENTS.md","isInternal":false,"tokens":3,"sizeBytes":9},{"name":"_category_.json","path":"docs/plugins/_category_.json","rawUrl":"https://raw.githubusercontent.com/infinitered/reactotron/HEAD/docs/plugins/_category_.json","title":"Plugins Skill","category":"anthropic-skill","format":"json","content":"{\n  \"label\": \"Plugins\",\n  \"position\": 5,\n  \"link\": {\n    \"type\": \"doc\",\n    \"id\": \"index\"\n  }\n}\n","frontmatter":{"label":"Plugins","position":5,"link":{"type":"doc","id":"index"}},"isInternal":false,"tokens":37,"sizeBytes":96},{"name":"apisauce.md","path":"docs/plugins/apisauce.md","rawUrl":"https://raw.githubusercontent.com/infinitered/reactotron/HEAD/docs/plugins/apisauce.md","title":"Plugins Skill","category":"anthropic-skill","format":"markdown","content":"---\ntitle: Apisauce\n---\n\n# reactotron-apisauce\n\n[Apisauce](http://github.com/skellock/apisauce) is a lightweight wrapper around the fantastic [Axios](https://github.com/mzabriskie/axios) networking library.\n\n![API Response](./images/apisauce/apisauce.jpg)\n\n## Installing\n\n```\nnpm i --save-dev reactotron-apisauce\n```\n\n```\nyarn add reactotron-apisauce -D\n```\n\n## Configuration\n\nTo use the apisauce plugin, add the additional plugin on the `import` line.\n\n```js\nimport apisaucePlugin from \"reactotron-apisauce\" //\n```\n\nThen plug it in when you configure Reactotron.\n\n```js\nReactotron.configure()\n  .use(\n    apisaucePlugin({\n      // ignoreContentTypes: /^(image)\\/.\\*$/i // <--- a way to skip printing the body of some requests (default is any image)\n    })\n  ) // <-- here we go!!!\n  .connect()\n```\n\n## Usage\n\nNext, wherever you create api functions for you application, bring in Reactotron and attach the monitor to your apisauce instance. Apisauce has a feature where you can attach a handler to watch.\n\n1. To view all requests/response flowing through your api\n\n```js\n//for web\nimport Reactotron from \"reactotron-react-js\"\n\n//for mobile\nimport Reactotron from \"reactotron-react-native\"\n\napi.addMonitor(Reactotron.apisauce)\n```\n\n2. To view only the requests/response that have a problem\n\n```js\n//if you just wanted to track on 500's\napi.addMonitor((response) => {\n  if (response.problem === \"SERVER_ERROR\") Reactotron.apisauce(response)\n})\n```\n","frontmatter":{"title":"Apisauce"},"isInternal":false,"tokens":368,"sizeBytes":1446},{"name":"async-storage.md","path":"docs/plugins/async-storage.md","rawUrl":"https://raw.githubusercontent.com/infinitered/reactotron/HEAD/docs/plugins/async-storage.md","title":"Plugins Skill","category":"anthropic-skill","format":"markdown","content":"---\ntitle: AsyncStorage\n---\n\n# AsyncStorage\n\nThe `asyncStorage` plugin is part of `reactotron-react-native` which allows you to track [AsyncStorage](https://facebook.github.io/react-native/docs/asyncstorage.html) on React Native.\n\n## Usage\n\nTo use the Async Storage plugin, add the additional plugin on the `import` line.\n\n```js\nimport Reactotron, { asyncStorage } from \"reactotron-react-native\"\n```\n\nNext, add it as a plugin to Reactotron.\n\n```js\nReactotron.configure()\n  .use(asyncStorage()) // <--- here we go!\n  .connect()\n```\n\nAnd you're done! Now you can see your AsyncStorage in Reactotron.\n\n## Advanced Usage\n\n`asyncStorage()` also accepts an object with an `ignore` key. The value is an array of strings you would like to prevent sending to Reactotron.\n\n```js\nasyncStorage({\n  ignore: [\"secret\"],\n})\n```\n","frontmatter":{"title":"AsyncStorage"},"isInternal":false,"tokens":194,"sizeBytes":813},{"name":"benchmark.md","path":"docs/plugins/benchmark.md","rawUrl":"https://raw.githubusercontent.com/infinitered/reactotron/HEAD/docs/plugins/benchmark.md","title":"Plugins Skill","category":"anthropic-skill","format":"markdown","content":"---\ntitle: Benchmarking\n---\n\n# Benchmarking\n\nIf you have a function that you believe is slow, you can benchmark it to find any bottlenecks like this:\n\n## Usage\n\n```js\nimport Reactotron from \"reactotron-react-native\"\n\nfunction slowFunction() {\n  const bench = Reactotron.benchmark(\"slow function benchmark\")\n\n  // Code that does thing A\n  bench.step(\"Thing A\")\n\n  // Code that does thing B\n  bench.step(\"Thing B\")\n\n  // Code that does thing C\n  bench.stop(\"Thing C\")\n}\n```\n\nNote that the last call is to `stop` instead of `step`! This is important, otherwise the benchmark results won't display in Reactotron.\n\nWhen `slowFunction` is invoked, Reactotron should show something like this:\n\n![Benchmarking Output](./images/benchmarking/benchmark-output.png)\n\nTips: Time bar `Thing B` is the code execution time from `bench.step(\"Thing A\")` to `bench.step(\"Thing B\")`.\n","frontmatter":{"title":"Benchmarking"},"isInternal":false,"tokens":210,"sizeBytes":864},{"name":"index.md","path":"docs/plugins/index.md","rawUrl":"https://raw.githubusercontent.com/infinitered/reactotron/HEAD/docs/plugins/index.md","title":"Plugins Skill","category":"anthropic-skill","format":"markdown","content":"# Reactotron Plugins\n\nReactotron has a powerful plugin system with ready-made plugins for you to use in your app. You can also create your own plugins to extend Reactotron's functionality.\n\n## What is a plugin?\n\nA plugin is a way to extend Reactotron's functionality. Plugins can be used to add new features to Reactotron or to add support for new libraries.\n\n## Installing a plugin\n\nTo install a plugin, you need to add it to your Reactotron client. You can do this by importing the plugin and passing it to the `use` method when you configure Reactotron.\n\n```js\nimport Reactotron from \"reactotron-react-native\"\n\nReactotron.configure()\n  .use(somePlugin())\n  .connect()\n```\n\n# Community-made plugins\n\nAnyone can make a Reactotron plugin! If you've made a plugin, please open a PR to add it to this list.\n\n> Note that Infinite Red does not maintain these plugins and can not guarantee compatibility or effectiveness. If you have questions about a plugin, please contact the plugin author.\n\n- [reactotron-react-query](https://github.com/hsndmr/reactotron-react-query)\n","isInternal":false,"tokens":232,"sizeBytes":1067},{"name":"mst.md","path":"docs/plugins/mst.md","rawUrl":"https://raw.githubusercontent.com/infinitered/reactotron/HEAD/docs/plugins/mst.md","title":"Plugins Skill","category":"anthropic-skill","format":"markdown","content":"---\ntitle: Mobx State Tree\n---\n\n# reactotron-mst\n\nBehold! A plugin for Reactotron for working with mobx-state-tree.\n\n# Prerequisites\n\nThis is a plugin for Reactotron, so you'll need either `reactotron-react-native` or `reactotron-react-dom` installed first.\n\nThis is also a plugin for `mobx-state-tree`, so you'll also need that installed as well.\n\n# Installation\n\nAdd a dev-dependency to `reactotron-mst` to your app.\n\n```sh\nyarn add reactotron-mst --dev\n```\n\nor\n\n```sh\nnpm i reactotron-mst --save-dev\n```\n\n# Configuration\n\nTo make Reactotron aware of this plugin, go to the file you are configuring Reactotron currently and add this:\n\n```ts\n// import the plugin\nimport { mst } from \"reactotron-mst\"\n\n// tell Reactotron to use this plugin\nReactotron.use(mst())\n```\n\nThis will bestow Reactotron the power to track `mobx-state-tree` nodes.\n\n# Usage\n\nFinally, you need to give `reactotron-mst` your root tree node.\n\nHere's an example of that in action:\n\n```ts\n// bring in Reactotron\nimport * as Reactotron from \"reactotron-react-native\"\n\n// bring in your mst model\nimport { MyModel } from \"./my-model\"\n\n// create an instance of your model\nconst myTree = MyModel.create()\n\n// let reactotron-mst know about it\nReactotron.trackMstNode(myTree)\n```\n\n# Options\n\nWhen you `use()` the `reactotron-mst`, you can also pass options.\n\n### filter\n\nThe `filter` property provides a way to control what is sent to Reactotron. It is a function which takes an [`IMiddlewareEvent`](https://github.com/mobxjs/mobx-state-tree/blob/master/docs/middleware.md#call-attributes) and returns a `boolean`. If you return `true`, the message will be sent to the Reactotron app. `false` will ignore this message.\n\nHere's an example which will stop all `postProcessSnapshot`-based actions from jumping the wire.\n\n```ts\nimport Tron from \"reactotron-react-native\"\nimport { mst } from \"reactotron-mst\"\n\nconst RX = /postProcessSnapshot/\nconst filter = (event) => RX.test(event.name) === false\n\nTron.use(mst({ filter }))\n```\n\nThe default value for `filter` if you don't provide it is `() => true`, which means everything gets passed to Reactotron.\n\n### queryMode\n\nThe `queryMode` property provides a way to switch between subscribing to live state or snapshots. The only time you'll want to subscribe to snapshots instead of live state is when verifying transitory state (via `postProcessSnapshot`) is not persisted.\n\n# Troubleshooting\n\nThe `trackMstNode()` function will only be available after you setup the `reactotron-mst` plugin. Make sure you do the previous setup step first or you'll see an error that says, `trackMstNode is not a function`.\n\n# Caveats\n\n### Phase 1\n\nThis plugin hooks into Reactotron just like the `redux` one. So the basics are in place, but it'll be a much nicer experience once we start introducing some custom views specifically for `mobx-state-tree`. Consider this plugin's status: phase 1 right now. 😅\n\n### Single Tree\n\nUnlike `redux`, `mobx-state-tree` doesn't have to have a single root node. I personally find it easier to set it up like this, though.\n\nCurrently, `reactotron-mst` only supports tracking 1 tree. Multi-tree support is planned, but requires a bit of retooling of the Reactotron app to support multiple states.\n\nAs a short-term hack, we might be able to find a way to do this by using a prefix to identify which tree you intend to work with. For example, if you would like to subscribe to a piece of state in a different tree, perhaps we could do something like this: `$tree3.currentUser.password`.\n\n### `flow()`-based actions issues\n\nSynchronous actions work well, however there's some issues with async actions when using the `mobx-state-tree` `flow()` function. We're going to have to introduce a new UI view similar to `redux-saga` to display this. In the meantime, the action which kicks off the flow will be logged immediate and the return value will be untracked (for now... sorry!).\n","frontmatter":{"title":"Mobx State Tree"},"isInternal":false,"tokens":942,"sizeBytes":3904},{"name":"networking.md","path":"docs/plugins/networking.md","rawUrl":"https://raw.githubusercontent.com/infinitered/reactotron/HEAD/docs/plugins/networking.md","title":"Plugins Skill","category":"anthropic-skill","format":"markdown","content":"---\ntitle: Networking\n---\n\n# Networking\n\nThe `networking` plugin is `reactotron-react-native` which allows you to track all XMLHttpRequests in React Native.\n\nOn Expo SDK 56+, `expo/fetch` is installed as the default `globalThis.fetch`. It is backed by a native module and bypasses `XMLHttpRequest`, so it would otherwise be invisible to this plugin. The plugin detects this case and tracks `expo/fetch` requests as well (see `ignoreExpoFetch` below to opt out). Requests made through React Native's regular XHR-backed `fetch` continue to be tracked via `XMLHttpRequest`, so there is no double-reporting.\n\n## Usage\n\nTo use the `networking` plugin, you need to add the additional plugin on the `import` line.\n\n```js\nimport Reactotron, { networking } from \"reactotron-react-native\";\n```\n\nNext, add it as a plugin to Reactotron.\n\n```js\nReactotron.configure()\n  .use(networking()) // <--- here we go!\n  .connect();\n```\n\nAnd you're done! Now you can see your XMLHttpRequests in Reactotron.\n\n## Advanced Usage\n\n`networking()` also accepts an object with these options:\n\n- `ignoreContentTypes`: a regular expression which, when matched against the `Content-Type` response header, will prevent the data from being displayed in Reactotron. You typically want to do this for images (which is the default). `text/event-stream` response bodies are always skipped so streaming responses are not buffered.\n- `ignoreUrls`: a regular expression which, when matched against the URL of the request, will prevent the request from being tracked in Reactotron. Can be useful for ignoring noisy logging requests.\n- `ignoreExpoFetch`: set to `true` to skip instrumenting Expo's `expo/fetch` (the default `globalThis.fetch` on Expo SDK 56+). Has no effect on non-Expo runtimes, where the global fetch is XHR-backed and already covered by XHR tracking.\n- `fetch`: explicitly pass the fetch function to track; it is wrapped and installed as `globalThis.fetch` on connect, skipping the automatic `expo/fetch` detection. Takes precedence over `ignoreExpoFetch`.\n\n```js\nnetworking({\n  ignoreContentTypes: /^(image)\\/.*$/i,\n  ignoreUrls: /\\/(logs|symbolicate)$/,\n});\n```\n\n### Tracking fetch in expo-router apps\n\nExpo Router (the default `create-expo-app` template) re-wraps `globalThis.fetch` at startup with its `window.location` polyfill, which drops the marker the automatic `expo/fetch` detection looks for — so on expo-router apps fetch requests are silently not tracked. Automatic detection for expo-router apps is in the works (tracked in [#1612](https://github.com/infinitered/reactotron/issues/1612)); until then, use the `fetch` option to track them:\n\n```js\nReactotron.configure()\n  .useReactNative({\n    networking: { fetch: globalThis.fetch },\n  })\n  .connect();\n```\n\nTwo caveats:\n\n- **Ordering**: capture `globalThis.fetch` in code that runs _after_ `expo-router/entry` has set up its wrapper (any module imported from your app code qualifies — the router entry runs first). Capturing it too early passes the pre-router fetch, and the router's wrapper will be bypassed or clobbered.\n- **No XHR-backed fetch**: only pass a fetch that does _not_ go through `XMLHttpRequest` (e.g. don't use this with `EXPO_PUBLIC_USE_RN_FETCH=1`). XHR-backed fetch is already tracked by the XHR interceptor, so wrapping it here would double-report every request.\n\nAlso note: Expo SDK 56 releases before 56.0.19 have a `Response.clone()` bug ([expo#46397](https://github.com/expo/expo/pull/46397)) where cloning a response twice can throw a spurious \"body already used\" error. Reactotron reads response bodies off a clone while tracking is active, so if your app also clones responses, upgrade to expo 56.0.19+ (or SDK 57).\n","frontmatter":{"title":"Networking"},"isInternal":false,"tokens":880,"sizeBytes":3690},{"name":"open-in-editor.md","path":"docs/plugins/open-in-editor.md","rawUrl":"https://raw.githubusercontent.com/infinitered/reactotron/HEAD/docs/plugins/open-in-editor.md","title":"Plugins Skill","category":"anthropic-skill","format":"markdown","content":"---\ntitle: Open In Editor\n---\n\n# Open In Editor\n\nThe `openInEditor` plugin allows you to click on a line of code in Reactotron and have it open in your editor.\n\n## Configuration\n\nTo use the `openInEditor` plugin, add the additional plugin on the `import` line.\n\n```js\nimport Reactotron, { openInEditor } from \"reactotron-react-native\"\n```\n\nNext, add it as a plugin to Reactotron.\n\n```js\nReactotron.configure()\n  .use(openInEditor()) // <--- here we go!\n  .connect()\n```\n","frontmatter":{"title":"Open In Editor"},"isInternal":false,"tokens":123,"sizeBytes":470},{"name":"overlay.md","path":"docs/plugins/overlay.md","rawUrl":"https://raw.githubusercontent.com/infinitered/reactotron/HEAD/docs/plugins/overlay.md","title":"Plugins Skill","category":"anthropic-skill","format":"markdown","content":"---\ntitle: Image Overlay\n---\n\n# Overlay\n\nOverlay is a plugin for `reactotron-react-native` which allows you to have an image uploaded\nto your simulator to stay on top of your app. This helps you to create those pixel perfect screens by helping you compare your app to the design.\n\n## Configuration\n\nTo use the Overlay plugin, add the additional plugin on the `import` line.\n\n```js\nimport Reactotron, { overlay } from \"reactotron-react-native\"\n```\n\nAdd it as a plugin to Reactotron.\n\n```js\nReactotron.configure()\n  .useReactNative({\n    overlay: true, // <--- here we go!\n  })\n  .connect()\n```\n\n## Usage\n\nNext, find the root UI component in your app and wrap it.\n\n```js\n// let's pretend this is your app.\nclass MyApp extends Component {\n  render() {\n    return <Text>I may have shipped too early.</Text>\n  }\n}\n\n// let's wrap it, so the overlay stays on top!\nconst MyAppWithBenefits = Reactotron.overlay(MyApp)\n\nexport default MyAppWithBenefits\n```\n\n# React Native Production Caveat\n\nOne common gotcha here is when you make production builds since `reactotron-react-native` is likely setup in `devDependencies`.\n\nIf you'd like to keep it like this (I recommend it!), then perhaps your code might look more like this:\n\n```js\nconst MyAppWithBenefits = __DEV__ ? Reactotron.overlay(MyApp) : MyApp\n```\n\nAnother option is to ship it in your `dependencies` (not recommended -- but sometimes you wanna run it on a device).\n","frontmatter":{"title":"Image Overlay"},"isInternal":false,"tokens":332,"sizeBytes":1414},{"name":"react-native-mmkv.md","path":"docs/plugins/react-native-mmkv.md","rawUrl":"https://raw.githubusercontent.com/infinitered/reactotron/HEAD/docs/plugins/react-native-mmkv.md","title":"Plugins Skill","category":"anthropic-skill","format":"markdown","content":"---\ntitle: MMKV\n---\n\n# React Native MMKV\n\nThe `reactotron-react-native-mmkv` plugin allows you to track [MMKV](https://github.com/mrousavy/react-native-mmkv) updates.\n\n## Installing\n\n```\nnpm i --save-dev reactotron-react-native-mmkv\n```\n\n```\nyarn add -D reactotron-react-native-mmkv\n```\n\n## Usage\n\nImport your MMKV storage instance:\n\n```tsx\nimport { MMKV } from \"react-native-mmkv\"\nexport const storage = new MMKV()\n```\n\nTo use the `mmkvPlugin`, add the additional plugin on the `import` line.\n\n```tsx\nimport Reactotron from \"reactotron-react-native\"\n// success-line\nimport type { ReactotronReactNative } from \"reactotron-react-native\"\n// success-line\nimport mmkvPlugin from \"reactotron-react-native-mmkv\"\n// success-line\nimport { storage } from \"./mmkv/storage/instance/location\" // <--- update this to your mmkv instance.\n...\nReactotron.configure()\n  // success-line\n  .use(mmkvPlugin<ReactotronReactNative>({ storage }))\n  .connect()\n```\n\nAnd you're done! Now you can see your MMKV in Reactotron.\n\n## Advanced Usage\n\n`mmkvPlugin()` accepts an object with an `ignore` key. The value is an array of strings you would like to prevent sending to Reactotron.\n\n```tsx\nmmkvPlugin<ReactotronReactNative>({\n  storage,\n  ignore: [\"secret\"],\n})\n```\n","frontmatter":{"title":"MMKV"},"isInternal":false,"tokens":324,"sizeBytes":1241},{"name":"redux.md","path":"docs/plugins/redux.md","rawUrl":"https://raw.githubusercontent.com/infinitered/reactotron/HEAD/docs/plugins/redux.md","title":"Plugins Skill","category":"anthropic-skill","format":"markdown","content":"---\ntitle: Redux\n---\n\n# reactotron-redux\n\n[Redux](http://redux.js.org/) is a library for managing global state. It's pretty damn awesome.\n\nAnd everyone knows when you combine two great things, you get a super-awesome thing. Except for toothpaste & orange juice. Keep those separate.\n\n![Keys and Values](./images/redux/redux-keys-values.jpg)\n\n![Dispatching](./images/redux/dispatching.jpg)\n\n![Subscriptions](./images/redux/subscriptions.jpg)\n\n# Installing\n\n`reactotron-redux` is a plugin which lives in a different npm package. You can install it by typing:\n\n```\nnpm install --save-dev reactotron-redux\n```\n\nor\n\n```\nyarn add reactotron-redux -D\n```\n\n# Features\n\n- track when actions are dispatched including timing\n- subscribe to changes within the state tree\n- pull values out on demand\n- view list of keys\n- dispatch actions from Reactotron\n- replay actions\n- hot swap your app state on the fly\n\n# Configuring\n\nTwo files need to change to hookup Reactotron to Redux. First, in your\nReactotronConfig, you'll need to add `reactotron-redux` as plugin\n\n```diff\n// ReactotronConfig.js\n+ import { reactotronRedux } from 'reactotron-redux'\n\n\n// then add it to the plugin list\n- Reactotron\n+ const reactotron = Reactotron\n  .configure({ name: 'React Native Demo' })\n+ .use(reactotronRedux()) //  <- here i am!\n  .connect() //Don't forget about me!\n\n+ export default reactotron // also: export me so I can be referenced by Redux store\n```\n\nNext, we'll add the enhancer from `Reactotron.createEnhancer()` in development.\n\n## Using Redux-Toolkit configureStore\n\nUsing [Redux-Toolkit's `configureStore`](https://redux-toolkit.js.org/api/configureStore), add the `enhancers` key.\n\n```tsx\nconst createEnhancers = (getDefaultEnhancers: GetDefaultEnhancers<any>) => {\n  if (__DEV__) {\n    const reactotron = require(\"../devtools/ReactotronConfig\").default\n    return getDefaultEnhancers().concat(reactotron.createEnhancer())\n  } else {\n    return getDefaultEnhancers()\n  }\n}\n\nexport const store = configureStore({\n  reducer: persistedReducer,\n  enhancers: createEnhancers,\n})\n```\n\n## Using Redux createStore\n\n```diff\nimport { createStore } from 'redux'\n+ import Reactotron from './ReactotronConfig'\n\n- const store = createStore(rootReducer)\n+ const store = createStore(rootReducer, Reactotron.createEnhancer())\n+ // or const store = createStore(rootReducer, preloadedState, Reactotron.createEnhancer())\n```\n\nNote: passing enhancer as last argument requires redux@>=3.1.0\n\n### If you have middleware\n\n```diff\nimport { createStore, compose } from 'redux'\n+ import Reactotron from './ReactotronConfig'\n\n- const store = createStore(rootReducer, compose(middleware))\n+ const store = createStore(rootReducer, compose(middleware, Reactotron.createEnhancer()))\n```\n\n# Options\n\n`reactotronRedux()` accepts an optional parameter which is an object you can use\nto configure\n\n#### except\n\n`except` is an array of strings that match actions flowing through Redux.\n\nIf you have some actions you'd rather just not see (for example, `redux-saga`\ntriggers a little bit of noise), you can suppress them:\n\n```js\nreactotronRedux({\n  except: [\"EFFECT_TRIGGERED\", \"EFFECT_RESOLVED\", \"EFFECT_REJECTED\"],\n})\n```\n\n#### isActionImportant\n\n`isActionImportant` is a function which receives an action and returns a boolean.\n`true` will cause the action to show up in the Reactotron app with a highlight.\n\n```js\nreactotronRedux({\n  isActionImportant: (action) => action.type === \"repo.receive\",\n})\n```\n\n#### onBackup\n\n`onBackup` fires when we're about to transfer a copy of your Redux global state\ntree and send it to the server. It accepts an object called `state` and returns\nan object called `state`.\n\nYou can use this to prevent big, sensitive, or transient data from going to\nReactotron.\n\n#### onRestore\n\n`onRestore` is the opposite of `onBackup`. It will fire when the Reactotron app\nsends a new copy of state to the app.\n","frontmatter":{"title":"Redux"},"isInternal":false,"tokens":929,"sizeBytes":3883},{"name":"storybook.md","path":"docs/plugins/storybook.md","rawUrl":"https://raw.githubusercontent.com/infinitered/reactotron/HEAD/docs/plugins/storybook.md","title":"Plugins Skill","category":"anthropic-skill","format":"markdown","content":"---\ntitle: Storybook\n---\n\n# Storybook (React Native Only)\n\n`reactotron-react-native` ships with a plugin called `storybook`.\n\nThis plugin, when configured properly, allows the switching to Storybook from the Reactotron interface\n\n## Configuration\n\nThe storybook plugin gets turned on automatically when you use `.useReactNative()`. If you don't want to use that method you can manually wire up the plugin:\n\nWherever you setup your Reactotron in your app, you also add the additional plugin on the `import` line.\n\n```js\nimport Reactotron, { storybook } from \"reactotron-react-native\"\n```\n\nNext, add it as a plugin to Reactotron.\n\n```js\nReactotron.configure()\n  .useReactNative({\n    storybook: true,\n  })\n  .connect()\n```\n\n## Usage\n\nOnce the plugin is configured you have to wrap your entire app in a HOC provided by Reactotron. In addition to that Storybook requires some configuration. Here is an example:\n\n```js\nimport { getStorybookUI, configure } from '@storybook/react-native'\n\n{...}\n\nconfigure(() => {\n  require('../../storybook/stories') // This should point to your \"root\" set of stories\n}, module)\n\nconst StorybookUIRoot = getStorybookUI({ port: 7007, onDeviceUI: true }) // This configuration can be changed based upon personal wants\n\nclass StorybookUIHMRRoot extends Component {\n  render () {\n    return <StorybookUIRoot />\n  }\n}\n\n// Assuming you have Reactotron on console.tron\nexport default console.tron.storybookSwitcher(StorybookUIHMRRoot)(RootContainer)\n```\n","frontmatter":{"title":"Storybook"},"isInternal":false,"tokens":346,"sizeBytes":1475},{"name":"track-global-errors.md","path":"docs/plugins/track-global-errors.md","rawUrl":"https://raw.githubusercontent.com/infinitered/reactotron/HEAD/docs/plugins/track-global-errors.md","title":"Plugins Skill","category":"anthropic-skill","format":"markdown","content":"---\ntitle: Global Errors\n---\n\n# Track Global Errors\n\nThe goal of this plugin is to ensure all errors are thrown over to Reactotron for display.\nBoth `reactotron-react-native` and `reactotron-react-js` ship with this plugin.\n\n# Usage\n\nTo use the `trackGlobalErrors` plugin, add the additional plugin on the `import` line.\n\n```js\nimport Reactotron, { trackGlobalErrors } from \"reactotron-react-native\";\n```\n\nor\n\n```js\nimport Reactotron, { trackGlobalErrors } from \"reactotron-react-js\";\n```\n\nNext, add it as a plugin to Reactotron.\n\n```js\nReactotron.configure()\n  .use(trackGlobalErrors()) // <--- here we go!\n  .connect();\n```\n\n## Options\n\nVeto is function that allows you to select frames you would like to leave out of the stack trace.\n\nFor example, on React Native, you'd like to leave off any frames sourced from React Native itself\n\n```js\nReactotron.configure()\n  .use(\n    trackGlobalErrors({\n      veto: (frame) =>\n        !frame.fileName.includes(\"/node_modules/react-native/\"),\n    })\n  )\n  .connect();\n```\n\n`veto` is a function that takes an `object` and returns a `boolean`. `true` = keep it. `false` = ditch it.\n\n> **Note:** This behavior is inverted from the originally intended design. The function was meant to work as \"true = ditch it, false = keep it\", but due to a long-standing implementation detail (using `.filter()` which keeps truthy values), the actual behavior has been the opposite. Since this behavior has existed for so long, it's now considered the expected behavior and the documentation has been updated to reflect it.\n\nThe frame object passed into `veto` has these properties.\n\n```\nfunctionName:  the name of the function or null if an anonymous function\nlineNumber:    the line number of the error\ncolumnNumber:  the column number of the error\nfileName:      the name of the file\n```\n\n### React JS Source Maps\n\nSource maps for projects in webpack need to have the `devtool` set to `source-map`. On `create-react-app` based apps, they use `eval`, so this will not work. You will have to switch your development webpack configuration to support this.\n\nYou also have the option to not do source map lookups, but still pass errors along by going into offline mode.\n\n```js\nReactotron.configure()\n  .use(\n    trackGlobalErrors({\n      offline: true,\n    })\n  )\n  .connect();\n```\n\n# How It Works Internally\n\n### React JS\n\nIt hijacks the browser's `window.onerror` event, immediately calling the previous `onerror`, then attempts to resolve the source of the errors via a source-map lookup. If successful, it throws a message over to Reactotron.\n\n### React Native\n\nOn React Native, it hooks `NativeModules.LogBox.addException`. This is an internal method that React Native uses internally to report errors to LogBox. It always calls the original function so it won't break anything internally. Your Red Box will still show.\n\nBecause we're hooking a Facebook internal function, this is a bit fragile. We'll stay on top of any React Native upgrades that might cause problems should the API change.\n","frontmatter":{"title":"Global Errors"},"isInternal":false,"tokens":691,"sizeBytes":3020},{"name":"track-global-logs.md","path":"docs/plugins/track-global-logs.md","rawUrl":"https://raw.githubusercontent.com/infinitered/reactotron/HEAD/docs/plugins/track-global-logs.md","title":"Plugins Skill","category":"anthropic-skill","format":"markdown","content":"---\ntitle: Global Logs\n---\n\n# Documentation: `trackGlobalLogs`\n\n## Overview\n\nThe `trackGlobalLogs` plugin for Reactotron is designed to intercept calls made to `console.log`, `console.warn`, and `console.debug`, forwarding them to the Reactotron logger. This functionality is useful for monitoring and analyzing logs within your React Native or React JS applications.\n\n### Usage\n\nTo integrate the `trackGlobalLogs` plugin into your Reactotron setup, follow these steps:\n\n#### Default\n\nThis plugin is included by default in Reactotron when using `reactotron-react-native`.\n\n```js\nimport Reactotron from \"reactotron-react-native\";\n\nReactotron.configure()\n  .useReactNative() // included by default\n  .connect();\n```\n\n#### Custom\n\nIf are not using the `useReactNative` method for some reason, you can add the plugin manually like so:\n\n1. Import the `trackGlobalLogs` function from the Reactotron package:\n\n```js\nimport Reactotron, { trackGlobalLogs } from \"reactotron-react-native\";\n```\n\n2. Add the `trackGlobalLogs` plugin to your Reactotron configuration as shown below:\n\n```js\nReactotron.configure().use(trackGlobalLogs()).connect();\n```\n\nThis enables the plugin to start intercepting global logs once your Reactotron instance is connected.\n\n### How It Works Internally\n\nFor both React Native and React JS applications, the `trackGlobalLogs` plugin works by monkey-patching references to `console.log`, `console.warn`, and `console.debug`. It intercepts calls to these console methods and, in addition to the original console output, also sends the log data to the Reactotron logger for further analysis.\n\nPlease note that there are no additional configuration options provided by the plugin itself. It automatically captures `console.log`, `console.warn`, and `console.debug` calls without any customization options.\n","frontmatter":{"title":"Global Logs"},"isInternal":false,"tokens":379,"sizeBytes":1818}],"systemPromptSnippet":"<yakaai_skills repo=\"infinitered/reactotron\">\n<!-- File: AGENTS.md (Tokens: ~169 | Category: root-instruction) -->\n# Agent Notes\n\nMinimal router — the linked docs are the source of truth.\n\n- **Monorepo layout, build, test**: [docs/contributing/monorepo.md](docs/contributing/monorepo.md) and [docs/contributing/architecture.md](docs/contributing/architecture.md)\n- **Verifying fixes at runtime** (real desktop dev build + simulator app, fully scriptable e2e — use this to verify PRs, not just unit tests): [docs/contributing/runtime-verification.md](docs/contributing/runtime-verification.md)\n- **CI and trusting fork PRs**: [docs/contributing/ci.md](docs/contributing/ci.md)\n- **Releasing**: [docs/contributing/releasing.md](docs/contributing/releasing.md)\n\n<!-- File: CLAUDE.md (Tokens: ~3 | Category: claude-rule) -->\nAGENTS.md\n\n<!-- File: docs/plugins/_category_.json (Tokens: ~37 | Category: anthropic-skill) -->\n{\n  \"label\": \"Plugins\",\n  \"position\": 5,\n  \"link\": {\n    \"type\": \"doc\",\n    \"id\": \"index\"\n  }\n}\n\n<!-- File: docs/plugins/apisauce.md (Tokens: ~368 | Category: anthropic-skill) -->\n---\ntitle: Apisauce\n---\n\n# reactotron-apisauce\n\n[Apisauce](http://github.com/skellock/apisauce) is a lightweight wrapper around the fantastic [Axios](https://github.com/mzabriskie/axios) networking library.\n\n![API Response](./images/apisauce/apisauce.jpg)\n\n## Installing\n\n```\nnpm i --save-dev reactotron-apisauce\n```\n\n```\nyarn add reactotron-apisauce -D\n```\n\n## Configuration\n\nTo use the apisauce plugin, add the additional plugin on the `import` line.\n\n```js\nimport apisaucePlugin from \"reactotron-apisauce\" //\n```\n\nThen plug it in when you configure Reactotron.\n\n```js\nReactotron.configure()\n  .use(\n    apisaucePlugin({\n      // ignoreContentTypes: /^(image)\\/.\\*$/i // <--- a way to skip printing the body of some requests (default is any image)\n    })\n  ) // <-- here we go!!!\n  .connect()\n```\n\n## Usage\n\nNext, wherever you create api functions for you application, bring in Reactotron and attach the monitor to your apisauce instance. Apisauce has a feature where you can attach a handler to watch.\n\n1. To view all requests/response flowing through your api\n\n```js\n//for web\nimport Reactotron from \"reactotron-react-js\"\n\n//for mobile\nimport Reactotron from \"reactotron-react-native\"\n\napi.addMonitor(Reactotron.apisauce)\n```\n\n2. To view only the requests/response that have a problem\n\n```js\n//if you just wanted to track on 500's\napi.addMonitor((response) => {\n  if (response.problem === \"SERVER_ERROR\") Reactotron.apisauce(response)\n})\n```\n\n<!-- File: docs/plugins/async-storage.md (Tokens: ~194 | Category: anthropic-skill) -->\n---\ntitle: AsyncStorage\n---\n\n# AsyncStorage\n\nThe `asyncStorage` plugin is part of `reactotron-react-native` which allows you to track [AsyncStorage](https://facebook.github.io/react-native/docs/asyncstorage.html) on React Native.\n\n## Usage\n\nTo use the Async Storage plugin, add the additional plugin on the `import` line.\n\n```js\nimport Reactotron, { asyncStorage } from \"reactotron-react-native\"\n```\n\nNext, add it as a plugin to Reactotron.\n\n```js\nReactotron.configure()\n  .use(asyncStorage()) // <--- here we go!\n  .connect()\n```\n\nAnd you're done! Now you can see your AsyncStorage in Reactotron.\n\n## Advanced Usage\n\n`asyncStorage()` also accepts an object with an `ignore` key. The value is an array of strings you would like to prevent sending to Reactotron.\n\n```js\nasyncStorage({\n  ignore: [\"secret\"],\n})\n```\n\n<!-- File: docs/plugins/benchmark.md (Tokens: ~210 | Category: anthropic-skill) -->\n---\ntitle: Benchmarking\n---\n\n# Benchmarking\n\nIf you have a function that you believe is slow, you can benchmark it to find any bottlenecks like this:\n\n## Usage\n\n```js\nimport Reactotron from \"reactotron-react-native\"\n\nfunction slowFunction() {\n  const bench = Reactotron.benchmark(\"slow function benchmark\")\n\n  // Code that does thing A\n  bench.step(\"Thing A\")\n\n  // Code that does thing B\n  bench.step(\"Thing B\")\n\n  // Code that does thing C\n  bench.stop(\"Thing C\")\n}\n```\n\nNote that the last call is to `stop` instead of `step`! This is important, otherwise the benchmark results won't display in Reactotron.\n\nWhen `slowFunction` is invoked, Reactotron should show something like this:\n\n![Benchmarking Output](./images/benchmarking/benchmark-output.png)\n\nTips: Time bar `Thing B` is the code execution time from `bench.step(\"Thing A\")` to `bench.step(\"Thing B\")`.\n\n<!-- File: docs/plugins/index.md (Tokens: ~232 | Category: anthropic-skill) -->\n# Reactotron Plugins\n\nReactotron has a powerful plugin system with ready-made plugins for you to use in your app. You can also create your own plugins to extend Reactotron's functionality.\n\n## What is a plugin?\n\nA plugin is a way to extend Reactotron's functionality. Plugins can be used to add new features to Reactotron or to add support for new libraries.\n\n## Installing a plugin\n\nTo install a plugin, you need to add it to your Reactotron client. You can do this by importing the plugin and passing it to the `use` method when you configure Reactotron.\n\n```js\nimport Reactotron from \"reactotron-react-native\"\n\nReactotron.configure()\n  .use(somePlugin())\n  .connect()\n```\n\n# Community-made plugins\n\nAnyone can make a Reactotron plugin! If you've made a plugin, please open a PR to add it to this list.\n\n> Note that Infinite Red does not maintain these plugins and can not guarantee compatibility or effectiveness. If you have questions about a plugin, please contact the plugin author.\n\n- [reactotron-react-query](https://github.com/hsndmr/reactotron-react-query)\n\n<!-- File: docs/plugins/mst.md (Tokens: ~942 | Category: anthropic-skill) -->\n---\ntitle: Mobx State Tree\n---\n\n# reactotron-mst\n\nBehold! A plugin for Reactotron for working with mobx-state-tree.\n\n# Prerequisites\n\nThis is a plugin for Reactotron, so you'll need either `reactotron-react-native` or `reactotron-react-dom` installed first.\n\nThis is also a plugin for `mobx-state-tree`, so you'll also need that installed as well.\n\n# Installation\n\nAdd a dev-dependency to `reactotron-mst` to your app.\n\n```sh\nyarn add reactotron-mst --dev\n```\n\nor\n\n```sh\nnpm i reactotron-mst --save-dev\n```\n\n# Configuration\n\nTo make Reactotron aware of this plugin, go to the file you are configuring Reactotron currently and add this:\n\n```ts\n// import the plugin\nimport { mst } from \"reactotron-mst\"\n\n// tell Reactotron to use this plugin\nReactotron.use(mst())\n```\n\nThis will bestow Reactotron the power to track `mobx-state-tree` nodes.\n\n# Usage\n\nFinally, you need to give `reactotron-mst` your root tree node.\n\nHere's an example of that in action:\n\n```ts\n// bring in Reactotron\nimport * as Reactotron from \"reactotron-react-native\"\n\n// bring in your mst model\nimport { MyModel } from \"./my-model\"\n\n// create an instance of your model\nconst myTree = MyModel.create()\n\n// let reactotron-mst know about it\nReactotron.trackMstNode(myTree)\n```\n\n# Options\n\nWhen you `use()` the `reactotron-mst`, you can also pass options.\n\n### filter\n\nThe `filter` property provides a way to control what is sent to Reactotron. It is a function which takes an [`IMiddlewareEvent`](https://github.com/mobxjs/mobx-state-tree/blob/master/docs/middleware.md#call-attributes) and returns a `boolean`. If you return `true`, the message will be sent to the Reactotron app. `false` will ignore this message.\n\nHere's an example which will stop all `postProcessSnapshot`-based actions from jumping the wire.\n\n```ts\nimport Tron from \"reactotron-react-native\"\nimport { mst } from \"reactotron-mst\"\n\nconst RX = /postProcessSnapshot/\nconst filter = (event) => RX.test(event.name) === false\n\nTron.use(mst({ filter }))\n```\n\nThe default value for `filter` if you don't provide it is `() => true`, which means everything gets passed to Reactotron.\n\n### queryMode\n\nThe `queryMode` property provides a way to switch between subscribing to live state or snapshots. The only time you'll want to subscribe to snapshots instead of live state is when verifying transitory state (via `postProcessSnapshot`) is not persisted.\n\n# Troubleshooting\n\nThe `trackMstNode()` function will only be available after you setup the `reactotron-mst` plugin. Make sure you do the previous setup step first or you'll see an error that says, `trackMstNode is not a function`.\n\n# Caveats\n\n### Phase 1\n\nThis plugin hooks into Reactotron just like the `redux` one. So the basics are in place, but it'll be a much nicer experience once we start introducing some custom views specifically for `mobx-state-tree`. Consider this plugin's status: phase 1 right now. 😅\n\n### Single Tree\n\nUnlike `redux`, `mobx-state-tree` doesn't have to have a single root node. I personally find it easier to set it up like this, though.\n\nCurrently, `reactotron-mst` only supports tracking 1 tree. Multi-tree support is planned, but requires a bit of retooling of the Reactotron app to support multiple states.\n\nAs a short-term hack, we might be able to find a way to do this by using a prefix to identify which tree you intend to work with. For example, if you would like to subscribe to a piece of state in a different tree, perhaps we could do something like this: `$tree3.currentUser.password`.\n\n### `flow()`-based actions issues\n\nSynchronous actions work well, however there's some issues with async actions when using the `mobx-state-tree` `flow()` function. We're going to have to introduce a new UI view similar to `redux-saga` to display this. In the meantime, the action which kicks off the flow will be logged immediate and the return value will be untracked (for now... sorry!).\n\n<!-- File: docs/plugins/networking.md (Tokens: ~880 | Category: anthropic-skill) -->\n---\ntitle: Networking\n---\n\n# Networking\n\nThe `networking` plugin is `reactotron-react-native` which allows you to track all XMLHttpRequests in React Native.\n\nOn Expo SDK 56+, `expo/fetch` is installed as the default `globalThis.fetch`. It is backed by a native module and bypasses `XMLHttpRequest`, so it would otherwise be invisible to this plugin. The plugin detects this case and tracks `expo/fetch` requests as well (see `ignoreExpoFetch` below to opt out). Requests made through React Native's regular XHR-backed `fetch` continue to be tracked via `XMLHttpRequest`, so there is no double-reporting.\n\n## Usage\n\nTo use the `networking` plugin, you need to add the additional plugin on the `import` line.\n\n```js\nimport Reactotron, { networking } from \"reactotron-react-native\";\n```\n\nNext, add it as a plugin to Reactotron.\n\n```js\nReactotron.configure()\n  .use(networking()) // <--- here we go!\n  .connect();\n```\n\nAnd you're done! Now you can see your XMLHttpRequests in Reactotron.\n\n## Advanced Usage\n\n`networking()` also accepts an object with these options:\n\n- `ignoreContentTypes`: a regular expression which, when matched against the `Content-Type` response header, will prevent the data from being displayed in Reactotron. You typically want to do this for images (which is the default). `text/event-stream` response bodies are always skipped so streaming responses are not buffered.\n- `ignoreUrls`: a regular expression which, when matched against the URL of the request, will prevent the request from being tracked in Reactotron. Can be useful for ignoring noisy logging requests.\n- `ignoreExpoFetch`: set to `true` to skip instrumenting Expo's `expo/fetch` (the default `globalThis.fetch` on Expo SDK 56+). Has no effect on non-Expo runtimes, where the global fetch is XHR-backed and already covered by XHR tracking.\n- `fetch`: explicitly pass the fetch function to track; it is wrapped and installed as `globalThis.fetch` on connect, skipping the automatic `expo/fetch` detection. Takes precedence over `ignoreExpoFetch`.\n\n```js\nnetworking({\n  ignoreContentTypes: /^(image)\\/.*$/i,\n  ignoreUrls: /\\/(logs|symbolicate)$/,\n});\n```\n\n### Tracking fetch in expo-router apps\n\nExpo Router (the default `create-expo-app` template) re-wraps `globalThis.fetch` at startup with its `window.location` polyfill, which drops the marker the automatic `expo/fetch` detection looks for — so on expo-router apps fetch requests are silently not tracked. Automatic detection for expo-router apps is in the works (tracked in [#1612](https://github.com/infinitered/reactotron/issues/1612)); until then, use the `fetch` option to track them:\n\n```js\nReactotron.configure()\n  .useReactNative({\n    networking: { fetch: globalThis.fetch },\n  })\n  .connect();\n```\n\nTwo caveats:\n\n- **Ordering**: capture `globalThis.fetch` in code that runs _after_ `expo-router/entry` has set up its wrapper (any module imported from your app code qualifies — the router entry runs first). Capturing it too early passes the pre-router fetch, and the router's wrapper will be bypassed or clobbered.\n- **No XHR-backed fetch**: only pass a fetch that does _not_ go through `XMLHttpRequest` (e.g. don't use this with `EXPO_PUBLIC_USE_RN_FETCH=1`). XHR-backed fetch is already tracked by the XHR interceptor, so wrapping it here would double-report every request.\n\nAlso note: Expo SDK 56 releases before 56.0.19 have a `Response.clone()` bug ([expo#46397](https://github.com/expo/expo/pull/46397)) where cloning a response twice can throw a spurious \"body already used\" error. Reactotron reads response bodies off a clone while tracking is active, so if your app also clones responses, upgrade to expo 56.0.19+ (or SDK 57).\n\n<!-- File: docs/plugins/open-in-editor.md (Tokens: ~123 | Category: anthropic-skill) -->\n---\ntitle: Open In Editor\n---\n\n# Open In Editor\n\nThe `openInEditor` plugin allows you to click on a line of code in Reactotron and have it open in your editor.\n\n## Configuration\n\nTo use the `openInEditor` plugin, add the additional plugin on the `import` line.\n\n```js\nimport Reactotron, { openInEditor } from \"reactotron-react-native\"\n```\n\nNext, add it as a plugin to Reactotron.\n\n```js\nReactotron.configure()\n  .use(openInEditor()) // <--- here we go!\n  .connect()\n```\n\n<!-- File: docs/plugins/overlay.md (Tokens: ~332 | Category: anthropic-skill) -->\n---\ntitle: Image Overlay\n---\n\n# Overlay\n\nOverlay is a plugin for `reactotron-react-native` which allows you to have an image uploaded\nto your simulator to stay on top of your app. This helps you to create those pixel perfect screens by helping you compare your app to the design.\n\n## Configuration\n\nTo use the Overlay plugin, add the additional plugin on the `import` line.\n\n```js\nimport Reactotron, { overlay } from \"reactotron-react-native\"\n```\n\nAdd it as a plugin to Reactotron.\n\n```js\nReactotron.configure()\n  .useReactNative({\n    overlay: true, // <--- here we go!\n  })\n  .connect()\n```\n\n## Usage\n\nNext, find the root UI component in your app and wrap it.\n\n```js\n// let's pretend this is your app.\nclass MyApp extends Component {\n  render() {\n    return <Text>I may have shipped too early.</Text>\n  }\n}\n\n// let's wrap it, so the overlay stays on top!\nconst MyAppWithBenefits = Reactotron.overlay(MyApp)\n\nexport default MyAppWithBenefits\n```\n\n# React Native Production Caveat\n\nOne common gotcha here is when you make production builds since `reactotron-react-native` is likely setup in `devDependencies`.\n\nIf you'd like to keep it like this (I recommend it!), then perhaps your code might look more like this:\n\n```js\nconst MyAppWithBenefits = __DEV__ ? Reactotron.overlay(MyApp) : MyApp\n```\n\nAnother option is to ship it in your `dependencies` (not recommended -- but sometimes you wanna run it on a device).\n\n<!-- File: docs/plugins/react-native-mmkv.md (Tokens: ~324 | Category: anthropic-skill) -->\n---\ntitle: MMKV\n---\n\n# React Native MMKV\n\nThe `reactotron-react-native-mmkv` plugin allows you to track [MMKV](https://github.com/mrousavy/react-native-mmkv) updates.\n\n## Installing\n\n```\nnpm i --save-dev reactotron-react-native-mmkv\n```\n\n```\nyarn add -D reactotron-react-native-mmkv\n```\n\n## Usage\n\nImport your MMKV storage instance:\n\n```tsx\nimport { MMKV } from \"react-native-mmkv\"\nexport const storage = new MMKV()\n```\n\nTo use the `mmkvPlugin`, add the additional plugin on the `import` line.\n\n```tsx\nimport Reactotron from \"reactotron-react-native\"\n// success-line\nimport type { ReactotronReactNative } from \"reactotron-react-native\"\n// success-line\nimport mmkvPlugin from \"reactotron-react-native-mmkv\"\n// success-line\nimport { storage } from \"./mmkv/storage/instance/location\" // <--- update this to your mmkv instance.\n...\nReactotron.configure()\n  // success-line\n  .use(mmkvPlugin<ReactotronReactNative>({ storage }))\n  .connect()\n```\n\nAnd you're done! Now you can see your MMKV in Reactotron.\n\n## Advanced Usage\n\n`mmkvPlugin()` accepts an object with an `ignore` key. The value is an array of strings you would like to prevent sending to Reactotron.\n\n```tsx\nmmkvPlugin<ReactotronReactNative>({\n  storage,\n  ignore: [\"secret\"],\n})\n```\n\n<!-- File: docs/plugins/redux.md (Tokens: ~929 | Category: anthropic-skill) -->\n---\ntitle: Redux\n---\n\n# reactotron-redux\n\n[Redux](http://redux.js.org/) is a library for managing global state. It's pretty damn awesome.\n\nAnd everyone knows when you combine two great things, you get a super-awesome thing. Except for toothpaste & orange juice. Keep those separate.\n\n![Keys and Values](./images/redux/redux-keys-values.jpg)\n\n![Dispatching](./images/redux/dispatching.jpg)\n\n![Subscriptions](./images/redux/subscriptions.jpg)\n\n# Installing\n\n`reactotron-redux` is a plugin which lives in a different npm package. You can install it by typing:\n\n```\nnpm install --save-dev reactotron-redux\n```\n\nor\n\n```\nyarn add reactotron-redux -D\n```\n\n# Features\n\n- track when actions are dispatched including timing\n- subscribe to changes within the state tree\n- pull values out on demand\n- view list of keys\n- dispatch actions from Reactotron\n- replay actions\n- hot swap your app state on the fly\n\n# Configuring\n\nTwo files need to change to hookup Reactotron to Redux. First, in your\nReactotronConfig, you'll need to add `reactotron-redux` as plugin\n\n```diff\n// ReactotronConfig.js\n+ import { reactotronRedux } from 'reactotron-redux'\n\n\n// then add it to the plugin list\n- Reactotron\n+ const reactotron = Reactotron\n  .configure({ name: 'React Native Demo' })\n+ .use(reactotronRedux()) //  <- here i am!\n  .connect() //Don't forget about me!\n\n+ export default reactotron // also: export me so I can be referenced by Redux store\n```\n\nNext, we'll add the enhancer from `Reactotron.createEnhancer()` in development.\n\n## Using Redux-Toolkit configureStore\n\nUsing [Redux-Toolkit's `configureStore`](https://redux-toolkit.js.org/api/configureStore), add the `enhancers` key.\n\n```tsx\nconst createEnhancers = (getDefaultEnhancers: GetDefaultEnhancers<any>) => {\n  if (__DEV__) {\n    const reactotron = require(\"../devtools/ReactotronConfig\").default\n    return getDefaultEnhancers().concat(reactotron.createEnhancer())\n  } else {\n    return getDefaultEnhancers()\n  }\n}\n\nexport const store = configureStore({\n  reducer: persistedReducer,\n  enhancers: createEnhancers,\n})\n```\n\n## Using Redux createStore\n\n```diff\nimport { createStore } from 'redux'\n+ import Reactotron from './ReactotronConfig'\n\n- const store = createStore(rootReducer)\n+ const store = createStore(rootReducer, Reactotron.createEnhancer())\n+ // or const store = createStore(rootReducer, preloadedState, Reactotron.createEnhancer())\n```\n\nNote: passing enhancer as last argument requires redux@>=3.1.0\n\n### If you have middleware\n\n```diff\nimport { createStore, compose } from 'redux'\n+ import Reactotron from './ReactotronConfig'\n\n- const store = createStore(rootReducer, compose(middleware))\n+ const store = createStore(rootReducer, compose(middleware, Reactotron.createEnhancer()))\n```\n\n# Options\n\n`reactotronRedux()` accepts an optional parameter which is an object you can use\nto configure\n\n#### except\n\n`except` is an array of strings that match actions flowing through Redux.\n\nIf you have some actions you'd rather just not see (for example, `redux-saga`\ntriggers a little bit of noise), you can suppress them:\n\n```js\nreactotronRedux({\n  except: [\"EFFECT_TRIGGERED\", \"EFFECT_RESOLVED\", \"EFFECT_REJECTED\"],\n})\n```\n\n#### isActionImportant\n\n`isActionImportant` is a function which receives an action and returns a boolean.\n`true` will cause the action to show up in the Reactotron app with a highlight.\n\n```js\nreactotronRedux({\n  isActionImportant: (action) => action.type === \"repo.receive\",\n})\n```\n\n#### onBackup\n\n`onBackup` fires when we're about to transfer a copy of your Redux global state\ntree and send it to the server. It accepts an object called `state` and returns\nan object called `state`.\n\nYou can use this to prevent big, sensitive, or transient data from going to\nReactotron.\n\n#### onRestore\n\n`onRestore` is the opposite of `onBackup`. It will fire when the Reactotron app\nsends a new copy of state to the app.\n\n<!-- File: docs/plugins/storybook.md (Tokens: ~346 | Category: anthropic-skill) -->\n---\ntitle: Storybook\n---\n\n# Storybook (React Native Only)\n\n`reactotron-react-native` ships with a plugin called `storybook`.\n\nThis plugin, when configured properly, allows the switching to Storybook from the Reactotron interface\n\n## Configuration\n\nThe storybook plugin gets turned on automatically when you use `.useReactNative()`. If you don't want to use that method you can manually wire up the plugin:\n\nWherever you setup your Reactotron in your app, you also add the additional plugin on the `import` line.\n\n```js\nimport Reactotron, { storybook } from \"reactotron-react-native\"\n```\n\nNext, add it as a plugin to Reactotron.\n\n```js\nReactotron.configure()\n  .useReactNative({\n    storybook: true,\n  })\n  .connect()\n```\n\n## Usage\n\nOnce the plugin is configured you have to wrap your entire app in a HOC provided by Reactotron. In addition to that Storybook requires some configuration. Here is an example:\n\n```js\nimport { getStorybookUI, configure } from '@storybook/react-native'\n\n{...}\n\nconfigure(() => {\n  require('../../storybook/stories') // This should point to your \"root\" set of stories\n}, module)\n\nconst StorybookUIRoot = getStorybookUI({ port: 7007, onDeviceUI: true }) // This configuration can be changed based upon personal wants\n\nclass StorybookUIHMRRoot extends Component {\n  render () {\n    return <StorybookUIRoot />\n  }\n}\n\n// Assuming you have Reactotron on console.tron\nexport default console.tron.storybookSwitcher(StorybookUIHMRRoot)(RootContainer)\n```\n\n<!-- File: docs/plugins/track-global-errors.md (Tokens: ~691 | Category: anthropic-skill) -->\n---\ntitle: Global Errors\n---\n\n# Track Global Errors\n\nThe goal of this plugin is to ensure all errors are thrown over to Reactotron for display.\nBoth `reactotron-react-native` and `reactotron-react-js` ship with this plugin.\n\n# Usage\n\nTo use the `trackGlobalErrors` plugin, add the additional plugin on the `import` line.\n\n```js\nimport Reactotron, { trackGlobalErrors } from \"reactotron-react-native\";\n```\n\nor\n\n```js\nimport Reactotron, { trackGlobalErrors } from \"reactotron-react-js\";\n```\n\nNext, add it as a plugin to Reactotron.\n\n```js\nReactotron.configure()\n  .use(trackGlobalErrors()) // <--- here we go!\n  .connect();\n```\n\n## Options\n\nVeto is function that allows you to select frames you would like to leave out of the stack trace.\n\nFor example, on React Native, you'd like to leave off any frames sourced from React Native itself\n\n```js\nReactotron.configure()\n  .use(\n    trackGlobalErrors({\n      veto: (frame) =>\n        !frame.fileName.includes(\"/node_modules/react-native/\"),\n    })\n  )\n  .connect();\n```\n\n`veto` is a function that takes an `object` and returns a `boolean`. `true` = keep it. `false` = ditch it.\n\n> **Note:** This behavior is inverted from the originally intended design. The function was meant to work as \"true = ditch it, false = keep it\", but due to a long-standing implementation detail (using `.filter()` which keeps truthy values), the actual behavior has been the opposite. Since this behavior has existed for so long, it's now considered the expected behavior and the documentation has been updated to reflect it.\n\nThe frame object passed into `veto` has these properties.\n\n```\nfunctionName:  the name of the function or null if an anonymous function\nlineNumber:    the line number of the error\ncolumnNumber:  the column number of the error\nfileName:      the name of the file\n```\n\n### React JS Source Maps\n\nSource maps for projects in webpack need to have the `devtool` set to `source-map`. On `create-react-app` based apps, they use `eval`, so this will not work. You will have to switch your development webpack configuration to support this.\n\nYou also have the option to not do source map lookups, but still pass errors along by going into offline mode.\n\n```js\nReactotron.configure()\n  .use(\n    trackGlobalErrors({\n      offline: true,\n    })\n  )\n  .connect();\n```\n\n# How It Works Internally\n\n### React JS\n\nIt hijacks the browser's `window.onerror` event, immediately calling the previous `onerror`, then attempts to resolve the source of the errors via a source-map lookup. If successful, it throws a message over to Reactotron.\n\n### React Native\n\nOn React Native, it hooks `NativeModules.LogBox.addException`. This is an internal method that React Native uses internally to report errors to LogBox. It always calls the original function so it won't break anything internally. Your Red Box will still show.\n\nBecause we're hooking a Facebook internal function, this is a bit fragile. We'll stay on top of any React Native upgrades that might cause problems should the API change.\n\n<!-- File: docs/plugins/track-global-logs.md (Tokens: ~379 | Category: anthropic-skill) -->\n---\ntitle: Global Logs\n---\n\n# Documentation: `trackGlobalLogs`\n\n## Overview\n\nThe `trackGlobalLogs` plugin for Reactotron is designed to intercept calls made to `console.log`, `console.warn`, and `console.debug`, forwarding them to the Reactotron logger. This functionality is useful for monitoring and analyzing logs within your React Native or React JS applications.\n\n### Usage\n\nTo integrate the `trackGlobalLogs` plugin into your Reactotron setup, follow these steps:\n\n#### Default\n\nThis plugin is included by default in Reactotron when using `reactotron-react-native`.\n\n```js\nimport Reactotron from \"reactotron-react-native\";\n\nReactotron.configure()\n  .useReactNative() // included by default\n  .connect();\n```\n\n#### Custom\n\nIf are not using the `useReactNative` method for some reason, you can add the plugin manually like so:\n\n1. Import the `trackGlobalLogs` function from the Reactotron package:\n\n```js\nimport Reactotron, { trackGlobalLogs } from \"reactotron-react-native\";\n```\n\n2. Add the `trackGlobalLogs` plugin to your Reactotron configuration as shown below:\n\n```js\nReactotron.configure().use(trackGlobalLogs()).connect();\n```\n\nThis enables the plugin to start intercepting global logs once your Reactotron instance is connected.\n\n### How It Works Internally\n\nFor both React Native and React JS applications, the `trackGlobalLogs` plugin works by monkey-patching references to `console.log`, `console.warn`, and `console.debug`. It intercepts calls to these console methods and, in addition to the original console output, also sends the log data to the Reactotron logger for further analysis.\n\nPlease note that there are no additional configuration options provided by the plugin itself. It automatically captures `console.log`, `console.warn`, and `console.debug` calls without any customization options.\n</yakaai_skills>"}