Skip to main content

Overview

The IHP.ViewSupport module provides the foundation for building views in IHP. It includes:
  • View type class and rendering functions
  • HSX templating support
  • Form helpers and CSS framework integration
  • Helper functions for URLs and paths
  • Time formatting utilities
This module is automatically available via IHP.ViewPrelude in all view files.

Core Types

View
Type Class
The main type class that all views must implement.
data ShowPostView = ShowPostView { post :: Post }

instance View ShowPostView where
    html ShowPostView { .. } = [hsx|
        <div class="post">
            <h1>{post.title}</h1>
            <p>{post.body}</p>
        </div>
    |]
Html
Type Alias
Type alias for HtmlWithContext ControllerContext. This is the return type of view rendering.
Layout
Type Alias
Type alias for Html -> Html. A layout wraps view content.
myLayout :: Layout
myLayout inner = [hsx|
    <!DOCTYPE html>
    <html>
        <body>
            <header>My App</header>
            {inner}
        </body>
    </html>
|]

View Type Class

html
theView -> Html
Render the view as HTML. Required method.
instance View IndexView where
    html IndexView { posts } = [hsx|
        <div class="posts">
            {forEach posts renderPost}
        </div>
    |]
beforeRender
theView -> IO ()
Hook that runs before rendering. Optional method.
instance View MyView where
    beforeRender view = do
        setLayout customLayout
    
    html MyView { .. } = [hsx|...|]
json
theView -> JSON.Value
Render the view as JSON. Optional method.
instance View ApiUserView where
    html view = error "Not implemented"
    json ApiUserView { user } = toJSON user

Template Helpers

forEach
Foldable f => f a -> (a -> Html) -> Html
Iterate over a collection and render each item.
[hsx|
    <ul>
        {forEach posts \post -> [hsx|
            <li>{post.title}</li>
        |]}
    </ul>
|]
currentViewId
Typeable view => Text
Get a string ID for the current view (e.g., “posts-show”).
-- In Web.View.Posts.Show
[hsx|<div id={currentViewId}>...</div>|]
-- Renders: <div id="posts-show">...</div>
nl2br
Textual text => text -> Html
Convert newlines to <br> tags.
[hsx|<p>{nl2br post.body}</p>|]
-- "Hello\nWorld" becomes "Hello<br/>World"
stripTags
Text -> Text
Remove HTML tags from text.
let clean = stripTags "<b>Hello</b> World"
-- Returns: "Hello World"

URL and Path Functions

pathTo
HasPath action => action -> Text
Generate a URL path for an action.
[hsx|<a href={pathTo ShowPostAction { postId }}>View Post</a>|]
-- Generates: <a href="/ShowPost?postId=...">View Post</a>
urlTo
HasPath action => action -> Text
Generate a complete URL for an action (includes domain).
let url = urlTo ShowPostAction { postId }
-- Returns: "https://example.com/ShowPost?postId=..."
isActivePath
PathString controller => controller -> Bool
Check if the current request path matches the given path.
[hsx|
    <a href={PostsAction} class={classes ["nav-link", ("active", isActivePath PostsAction)]}>
        Posts
    </a>
|]
isActivePathOrSub
PathString controller => controller -> Bool
Check if the current path starts with the given path.
-- If current path is /Posts/123
isActivePathOrSub PostsAction -- Returns: True
isActiveController
Data controller => controller -> Bool
Check if the current controller matches.
when (isActiveController PostsController) $ ...
isActiveAction
HasPath action => action -> Bool
Check if the current action matches.
[hsx|
    <li class={classes [("active", isActiveAction ShowPostAction { postId })]}>
        ...
    </li>
|]

Asset Management

assetPath
Text -> Text
Add cache buster to asset paths.
[hsx|<script src={assetPath "/app.js"}></script>|]
-- Renders: <script src="/app.js?v=9be8995c..."></script>
assetVersion
Text
Get the current asset version string.
let version = assetVersion
-- Returns: "9be8995c-7055-43d9-a1b2-43e05c210271"
Configure via the IHP_ASSET_VERSION environment variable.

Request Access

theRequest
Request
Access the current WAI Request.
let path = theRequest.rawPathInfo
let method = theRequest.requestMethod

CSS Framework Integration

theCSSFramework
CSSFramework
Get the current CSS framework configuration.
let framework = theCSSFramework
fromCSSFramework
KnownSymbol field => Proxy field -> appliedFunction
Access a CSS framework function.
let submitClass = fromCSSFramework @"submitClass"

Form Helpers

From IHP.View.Form:
formFor
record -> (FormContext record -> Html) -> Html
Create a form for a record with automatic CSRF protection.
renderForm :: Post -> Html
renderForm post = formFor post \form -> [hsx|
    {textField #title}
    {textField #body}
    {submitButton}
|]
textField
KnownSymbol fieldName => Proxy fieldName -> Html
Render a text input field.
{textField #email}
submitButton
Html
Render a submit button.
{submitButton { label = "Create Post" }}
selectField
KnownSymbol fieldName => Proxy fieldName -> [a] -> Html
Render a select dropdown.
{selectField #status ["draft", "published", "archived"]}
checkboxField
KnownSymbol fieldName => Proxy fieldName -> Html
Render a checkbox.
{checkboxField #isPublished}
hiddenField
KnownSymbol fieldName => Proxy fieldName -> Html
Render a hidden input field.
{hiddenField #userId}

Time Formatting

From IHP.View.TimeAgo:
timeAgo
UTCTime -> Html
Render a time as “5 minutes ago”, “2 hours ago”, etc.
[hsx|<span>Posted {timeAgo post.createdAt}</span>|]
-- Renders: <span>Posted 5 minutes ago</span>

Validation Support

getValidationFailure
KnownSymbol field => Proxy field -> Maybe Text
Get validation error for a field.
case getValidationFailure #email of
    Just error -> [hsx|<div class="error">{error}</div>|]
    Nothing -> mempty
hasValidationError
KnownSymbol field => Proxy field -> Bool
Check if a field has a validation error.
let inputClass = if hasValidationError #email
    then "form-control is-invalid"
    else "form-control"

Flash Messages

renderFlashMessages
Html
Render all flash messages.
-- In layout:
[hsx|
    <body>
        {renderFlashMessages}
        {inner}
    </body>
|]

Auto Refresh

From IHP.AutoRefresh.View:
autoRefresh
Html
Enable auto-refresh for the current view.
instance View DashboardView where
    html DashboardView { .. } = [hsx|
        {autoRefresh}
        <div>Dashboard content that updates in real-time</div>
    |]
From IHP.Modal.ViewFunctions:
modal
Html -> Html
Render content in a modal.
[hsx|
    {modal [hsx|
        <h2>Delete Confirmation</h2>
        <p>Are you sure?</p>
    |]}
|]

Live Reload

liveReloadWebsocketUrl
Text
Get the WebSocket URL for live reload in development.
let wsUrl = liveReloadWebsocketUrl
-- Returns: "ws://localhost:8001"

HSX Helpers

hsx
QuasiQuoter
Quasi-quoter for HSX templates.
[hsx|<div class="container">{content}</div>|]
toHtml
ToHtml a => a -> Html
Convert a value to HTML.
{toHtml "Hello World"}
{toHtml 42}
preEscapedToHtml
Text -> Html
Insert raw HTML without escaping.
{preEscapedToHtml "<strong>Bold</strong>"}

See Also

Build docs developers (and LLMs) love