By default, HyperViews are stateless. Nothing is stored in the server connection. HyperView updates are the direct result of processing the Action.
instance HyperView Swapper es where
  data Action Swapper = Hello | Goodbye
    deriving (Generic, ViewAction)

  update Hello = pure "Hello"
  update Goodbye = pure "Goodbye"
The simplest way to add state to a HyperView is to pass it back and forth between the Action and the View. In this implementation of the classic counter example, each constructor of the Action expects an Int, which represents the current count:
instance HyperView Counter es where
  data Action Counter
    = Increment Int
    | Decrement Int
    deriving (Generic, ViewAction)

  update (Increment n) = do
    pure $ viewCount (n + 1)
  update (Decrement n) = do
    pure $ viewCount (n - 1)
Our View Function also expects the current count as an input. It includes it in each Action, and the cycle continues:
viewCount :: Int -> View Counter ()
viewCount n = row $ do
  col ~ gap 10 $ do
    el ~ dataFeature $ text $ pack $ show n
    row ~ gap 10 $ do
      button (Decrement n) "Decrement" ~ Style.btn
      button (Increment n) "Increment" ~ Style.btn
Hyperbole can manage action-threaded state automatically by setting ViewState in your ViewId:
data Counter = CounterState
  deriving (Generic)
instance ViewId Counter where
  type ViewState Counter = Int
Instead of hyper, use hyperState to embed the HyperView with a starting state:
page :: (Hyperbole :> es) => Page es '[Counter]
page = do
  pure $ do
    hyperState CounterState 1 viewCount
State (ViewState viewId) :> es is already included in update, and can be accessed with familiar functions like get put and modify from Effectful.State.Dynamic
instance HyperView Counter es where
  data Action Counter
    = Increment
    | Decrement
    deriving (Generic, ViewAction)

  update Increment = do
    modify @Int (+ 1)
    pure viewCount
  update Decrement = do
    modify @Int (subtract 1)
    pure viewCount
To read the state in View use the viewState function
viewCount :: View Counter ()
viewCount = row $ do
  n <- viewState
  col ~ gap 10 $ do
    el ~ dataFeature $ text $ pack $ show n
    row ~ gap 10 $ do
      button Decrement "Decrement" ~ btn
      button Increment "Increment" ~ btn
The state Action threading and ViewState both live in on the web page itself, and are reset when the user navigates away or refreshes the browser.
Using the Hyperbole effect, we can store state in the browser Query string. This is useful for faceted search, or any time a user might want to share a url and have the page load with local state changes.
data Preferences = Preferences
  { message :: Text
  , color :: AppColor
  }
  deriving (Generic, Show, ToQuery, FromQuery)
instance Default Preferences where
  def = Preferences mempty def
Access the current query (or the default) using query, or work directly with params. Save changes using modifyQuery, setQuery, setParam, etc
State stored in the Query is page-wide, and survives refreshes or as long as the url is available.
Likewise we can store state in a browser cookie using Session. This is useful for user preferences, login state, and any time you want client-specific state to persist across navigation and refreshes
data Preferences = Preferences
  { message :: Text
  , color :: AppColor
  }
  deriving (Generic, Show, ToEncoded, FromEncoded, Session)
instance Default Preferences where
  def = Preferences "_" White
Access the current session (or the default) using session, or manipulate it with saveSession, deleteSession, etc
State stored in the Session is page-specific by default, but can be configured to be application-wide. It survives until manually cleared by the user or the application.
For any real application, most persistent state will need to use a separate Effect, like a database. In Side Effects we demonstrated how to use a custom effect to wrap a database.
Another way to store application-wide state is to use the Concurrent effect. It gives us the ability to work with TVars, MVars and STM. Here is another counter, implemented using Reader (TVar Int)
page :: (Hyperbole :> es, Concurrent :> es, Reader (TVar Int) :> es) => Page es '[Counter]
page = do
  n <- getCount
  pure $ do
    hyper Counter (viewCount n)

getCount :: (Concurrent :> es, Reader (TVar Int) :> es) => Eff es Int
getCount = readTVarIO =<< ask
Notice how this state is shared among all users application-wide, and survives until the app restarts