Side-effects for hyper actions.
Provides escape hatches for actions that need to do more than mutate cursors/signals. Most action logic should use cursors (the UI is a pure function of state), but some operations genuinely require side-effects that can't be expressed as state:
navigate! — client-side route change (pushState) from an actionset-cookie! — set an HTTP cookie on the action responsedelete-cookie! — remove an HTTP cookieexecute-script! — run arbitrary JS on the client via SSEThese are intentionally few. If you're reaching for execute-script! to update UI, consider whether a cursor would be more appropriate.
Effects are accumulated during action execution in a dynamic var and processed by the action handler after the action completes. Cookies are applied to the HTTP response; scripts are sent via SSE.
Example: (require '[hyper.effects :as effects])
;; Navigate after saving (h/action (save-post! data) (effects/navigate! :post-detail {:id 123}))
;; Set a cookie (h/action (when (authenticate! user pass) (effects/set-cookie! "auth" jwt {:http-only true :max-age 86400}) (effects/navigate! :dashboard)))
;; Run client-side JS (use sparingly) (h/action (effects/execute-script! "document.getElementById('search').focus()"))
Side-effects for hyper actions.
Provides escape hatches for actions that need to do more than mutate
cursors/signals. Most action logic should use cursors (the UI is a
pure function of state), but some operations genuinely require
side-effects that can't be expressed as state:
- `navigate!` — client-side route change (pushState) from an action
- `set-cookie!` — set an HTTP cookie on the action response
- `delete-cookie!` — remove an HTTP cookie
- `execute-script!` — run arbitrary JS on the client via SSE
These are intentionally few. If you're reaching for execute-script!
to update UI, consider whether a cursor would be more appropriate.
Effects are accumulated during action execution in a dynamic var and
processed by the action handler after the action completes. Cookies
are applied to the HTTP response; scripts are sent via SSE.
Example:
(require '[hyper.effects :as effects])
;; Navigate after saving
(h/action
(save-post! data)
(effects/navigate! :post-detail {:id 123}))
;; Set a cookie
(h/action
(when (authenticate! user pass)
(effects/set-cookie! "auth" jwt {:http-only true :max-age 86400})
(effects/navigate! :dashboard)))
;; Run client-side JS (use sparingly)
(h/action
(effects/execute-script! "document.getElementById('search').focus()"))Accumulator for effects emitted during action execution.
Bound to (atom {:cookies {} :scripts []}) by the action handler. nil outside action execution.
Accumulator for effects emitted during action execution.
Bound to (atom {:cookies {} :scripts []}) by the action handler.
nil outside action execution.(apply-cookies-to-response response pending-effects)Merge pending cookies into an HTTP response map. Returns the response with :cookies added/merged.
Merge pending cookies into an HTTP response map. Returns the response with :cookies added/merged.
(collect-pending!)Drain and return the accumulated effects. Returns a map with :cookies and :scripts. Called by action-handler after action execution.
Drain and return the accumulated effects. Returns a map with :cookies and :scripts. Called by action-handler after action execution.
(delete-cookie! name)(delete-cookie! name opts)Delete an HTTP cookie from within an action.
Sets the cookie with an empty value and max-age 0, which instructs the browser to remove it.
name: Cookie name string opts: Optional map — typically :path must match the original cookie's path
Example: (h/action (effects/delete-cookie! "auth") (effects/navigate! :login))
Delete an HTTP cookie from within an action.
Sets the cookie with an empty value and max-age 0, which instructs
the browser to remove it.
name: Cookie name string
opts: Optional map — typically :path must match the original cookie's path
Example:
(h/action
(effects/delete-cookie! "auth")
(effects/navigate! :login))(execute-script! js)Execute JavaScript on the client from within an action.
The script is sent via SSE as a Datastar execute-script event. Use sparingly — most UI updates are better expressed as cursor mutations that the render function responds to.
Legitimate uses: focusing an element, scrolling to a position, triggering a file download, clipboard operations.
js: JavaScript string to execute in the browser
Example: (h/action (effects/execute-script! "document.getElementById('search').focus()"))
(h/action (effects/execute-script! "window.scrollTo(0, 0)"))
Execute JavaScript on the client from within an action.
The script is sent via SSE as a Datastar execute-script event.
Use sparingly — most UI updates are better expressed as cursor
mutations that the render function responds to.
Legitimate uses: focusing an element, scrolling to a position,
triggering a file download, clipboard operations.
js: JavaScript string to execute in the browser
Example:
(h/action
(effects/execute-script! "document.getElementById('search').focus()"))
(h/action
(effects/execute-script! "window.scrollTo(0, 0)"))(format-execute-script-event js)Format a JavaScript string as a Datastar patch-elements SSE event that appends a self-removing <script> tag to the body.
Datastar doesn't have a dedicated execute-script event type. Instead, script execution is done by patching a <script> element into the DOM using the 'append' mode and 'body' selector. The script removes itself after execution via a data-effect attribute.
Format a JavaScript string as a Datastar patch-elements SSE event that appends a self-removing <script> tag to the body. Datastar doesn't have a dedicated execute-script event type. Instead, script execution is done by patching a <script> element into the DOM using the 'append' mode and 'body' selector. The script removes itself after execution via a data-effect attribute.
(format-pending-scripts pending-effects)Format all pending scripts as SSE events. Returns a single string of concatenated SSE events, or nil if there are no scripts.
Format all pending scripts as SSE events. Returns a single string of concatenated SSE events, or nil if there are no scripts.
(init-pending)Create a fresh pending effects atom. Called by action-handler before binding pending.
Create a fresh pending effects atom. Called by action-handler before binding *pending*.
(navigate! route-name)(navigate! route-name params)(navigate! route-name params query-params)Navigate to a named route from within an action.
Performs the server-side route transition (updates render-fn and route state, which triggers a re-render via SSE) and queues a pushState call to update the browser URL bar.
route-name: Keyword name of the route params: Optional map of path parameters query-params: Optional map of query parameters
Example: (h/action (let [post (save-post! data)] (effects/navigate! :post-detail {:id (:id post)})))
(h/action (effects/navigate! :search {} {:q "clojure"}))
Navigate to a named route from within an action.
Performs the server-side route transition (updates render-fn and route
state, which triggers a re-render via SSE) and queues a pushState
call to update the browser URL bar.
route-name: Keyword name of the route
params: Optional map of path parameters
query-params: Optional map of query parameters
Example:
(h/action
(let [post (save-post! data)]
(effects/navigate! :post-detail {:id (:id post)})))
(h/action
(effects/navigate! :search {} {:q "clojure"}))(set-cookie! name value)(set-cookie! name value opts)Set an HTTP cookie from within an action.
The cookie will be added to the action's HTTP response via Set-Cookie headers. This is the only way to set cookies from hyper — the SSE channel cannot carry Set-Cookie headers.
name: Cookie name string value: Cookie value string opts: Optional map of cookie options: :path — Cookie path (default "/") :max-age — Max age in seconds :http-only — Boolean, prevent JS access (default false) :secure — Boolean, HTTPS only (default false) :same-site — :strict, :lax, or :none
Example: (h/action (when-let [token (authenticate! user pass)] (effects/set-cookie! "auth" token {:http-only true :secure true :max-age (* 60 60 24 7)})))
Set an HTTP cookie from within an action.
The cookie will be added to the action's HTTP response via Set-Cookie
headers. This is the only way to set cookies from hyper — the SSE
channel cannot carry Set-Cookie headers.
name: Cookie name string
value: Cookie value string
opts: Optional map of cookie options:
:path — Cookie path (default "/")
:max-age — Max age in seconds
:http-only — Boolean, prevent JS access (default false)
:secure — Boolean, HTTPS only (default false)
:same-site — :strict, :lax, or :none
Example:
(h/action
(when-let [token (authenticate! user pass)]
(effects/set-cookie! "auth" token {:http-only true
:secure true
:max-age (* 60 60 24 7)})))cljdoc builds & hosts documentation for Clojure/Script libraries
| Ctrl+k | Jump to recent docs |
| ← | Move to previous article |
| → | Move to next article |
| Ctrl+/ | Jump to the search field |