> ## Documentation Index
> Fetch the complete documentation index at: https://developers.hubspot.com/docs/llms.txt
> Use this file to discover all available pages before exploring further.

---
id: f5948a73-caf9-49f4-bf47-53643a6d5f7c
---

# no-browser-storage

> Prevent usage of browser storage APIs (localStorage and sessionStorage) in UI extensions.

The `no-browser-storage` rule prevents usage of browser storage APIs ([localStorage](https://developer.mozilla.org/en-US/docs/Web/API/Window/localStorage) and [sessionStorage](https://developer.mozilla.org/en-US/docs/Web/API/Window/sessionStorage)).

## Rule details

UI extensions run in a sandboxed web worker environment where browser storage APIs (`localStorage` and `sessionStorage`) are intentionally unavailable for security and isolation. This prevents extensions from accessing storage data from the host application, storing sensitive data without proper security controls, or interfering with other extensions.

Storage APIs don't exist at runtime and will throw errors if accessed. Instead, use React state for temporary data or backend APIs for persistent data.

## Storage alternatives

Use the following UI extension alternatives instead of browser storage APIs:

| Use Case               | Alternative                                                                                                            | Purpose                    |
| ---------------------- | ---------------------------------------------------------------------------------------------------------------------- | -------------------------- |
| Temporary session data | [React state](https://react.dev/learn/managing-state) (`useState`, `useReducer`)                                       | Store component state      |
| Persistent user data   | [`hubspot.fetch()`](/apps/developer-platform/add-features/ui-extensions/ui-extensions-sdk#hubspot-fetch) + backend API | Store data across sessions |
| Complex app state      | [React Context](https://react.dev/reference/react/createContext) + `useReducer`                                        | Manage shared state        |
| API response caching   | React state with [custom hooks](https://react.dev/learn/reusing-logic-with-custom-hooks)                               | Cache data in memory       |

See the next section for an example of each alternative.

## Examples

### Temporary data

Instead of using `sessionStorage` for temporary form data:

```jsx theme={null}
// Incorrect
function cacheApiResponse(endpoint, data) {
  sessionStorage.setItem(`cache:${endpoint}`, JSON.stringify(data));
}

const Extension = () => {
  const cached = sessionStorage.getItem('formData');
  return <Text>{cached}</Text>;
};
```

Use React state:

```jsx theme={null}
import { useState } from 'react';
import { Input, Button, Flex } from '@hubspot/ui-extensions';

const Extension = () => {
  const [formData, setFormData] = useState({ name: '', email: '' });

  return (
    <Flex direction="column" gap="small">
      <Input
        label="Name"
        value={formData.name}
        onChange={(value) => setFormData({ ...formData, name: value })}
      />
      <Input
        label="Email"
        value={formData.email}
        onChange={(value) => setFormData({ ...formData, email: value })}
      />
      <Button onClick={() => console.log(formData)}>Submit</Button>
    </Flex>
  );
};
```

### Persistent data

Instead of using `localStorage` for persistent user preferences:

```jsx theme={null}
// Incorrect
const savePreferences = (theme) => {
  localStorage.setItem('userPrefs', JSON.stringify({ theme }));
};

const Extension = () => {
  const [theme, setTheme] = useState(() => {
    const saved = localStorage.getItem('userPrefs');
    return saved ? JSON.parse(saved).theme : 'light';
  });

  return <Text>Theme: {theme}</Text>;
};
```

Use [`hubspot.fetch()`](/apps/developer-platform/add-features/ui-extensions/ui-extensions-sdk#hubspot-fetch) to persist data via your backend:

```jsx theme={null}
import { useState, useEffect } from 'react';
import { hubspot, logger } from '@hubspot/ui-extensions';
import { Button, Text, LoadingSpinner, Flex } from '@hubspot/ui-extensions';

const Extension = () => {
  const [settings, setSettings] = useState(null);
  const [loading, setLoading] = useState(true);

  useEffect(() => {
    const loadSettings = async () => {
      try {
        const response = await hubspot.fetch('/api/user/preferences');
        const data = await response.json();
        setSettings(data);
      } catch (error) {
        logger.error('Failed to load settings', error);
      } finally {
        setLoading(false);
      }
    };

    loadSettings();
  }, []);

  const updateSetting = async (key, value) => {
    try {
      await hubspot.fetch('/api/user/preferences', {
        method: 'PUT',
        headers: { 'Content-Type': 'application/json' },
        body: JSON.stringify({ [key]: value }),
      });
      setSettings({ ...settings, [key]: value });
    } catch (error) {
      logger.error('Failed to update setting', error);
    }
  };

  if (loading) return <LoadingSpinner />;

  return (
    <Flex direction="column" gap="small">
      <Text>Theme: {settings?.theme || 'default'}</Text>
      <Button onClick={() => updateSetting('theme', 'dark')}>
        Switch to Dark
      </Button>
    </Flex>
  );
};
```

### Complex state management

Instead of using `localStorage` for app-wide state:

```jsx theme={null}
// Incorrect
const AppState = {
  save: (key, value) => {
    const state = JSON.parse(localStorage.getItem('appState') || '{}');
    state[key] = value;
    localStorage.setItem('appState', JSON.stringify(state));
  },
  get: (key) => {
    const state = JSON.parse(localStorage.getItem('appState') || '{}');
    return state[key];
  }
};
```

Use React Context with `useReducer`:

```jsx theme={null}
import { createContext, useContext, useReducer } from 'react';
import { hubspot } from '@hubspot/ui-extensions';
import { Button, Text } from '@hubspot/ui-extensions';

const AppStateContext = createContext();

const initialState = {
  cache: {},
  settings: {},
};

function stateReducer(state, action) {
  switch (action.type) {
    case 'CACHE_DATA':
      return { ...state, cache: { ...state.cache, [action.key]: action.data } };
    case 'UPDATE_SETTINGS':
      return { ...state, settings: { ...state.settings, ...action.payload } };
    default:
      return state;
  }
}

const AppStateProvider = ({ children }) => {
  const [state, dispatch] = useReducer(stateReducer, initialState);

  return (
    <AppStateContext.Provider value={{ state, dispatch }}>
      {children}
    </AppStateContext.Provider>
  );
};

const DataComponent = () => {
  const { state, dispatch } = useContext(AppStateContext);

  const cacheData = (key, data) => {
    dispatch({ type: 'CACHE_DATA', key, data });
  };

  return (
    <>
      <Text>Cached items: {Object.keys(state.cache).length}</Text>
      <Button onClick={() => cacheData('user', { name: 'John' })}>
        Cache User Data
      </Button>
    </>
  );
};

hubspot.extend(() => (
  <AppStateProvider>
    <DataComponent />
  </AppStateProvider>
));
```

## Related resources

* [UI extensions SDK overview](/apps/developer-platform/add-features/ui-extensions/ui-extensions-sdk)
* [hubspot.fetch() API](/apps/developer-platform/add-features/ui-extensions/ui-extensions-sdk#hubspot-fetch)
* [React state management](https://react.dev/learn/managing-state)
