Hyperscript

_hyperscript logo

Overview

_hyperscript is programming language that can be used in HTML files to implement interactive features such as event handling. It also supports asynchronous operations such as fetching data from a server by sending an HTTP request.

_hyperscript is based on the HyperTalk language which was used in Apple's HyperCard. From Wikipedia, HyperCard "is among the first successful hypermedia systems predating the World Wide Web."

Like HyperTalk, _hyperscript uses an English-like syntax. It emphasizes readability, but may feel more difficult to write at first because the syntax is quite different from typical programming languages.

_hyperscript is similar to Alpine and HTMX in that they add attributes to HTML. But hyperscript only adds one attribute whose name is a single underscore.

Placing code on HTML elements favors locality of behavior over separation of concerns much like Tailwind, Alpine, and HTMX.

_hyperscript was created by Carson Gross who also created htmx. As of March 2024 it had not yet reached version 1.0.

Installing

To use _hyperscript, just include the following script tag in each HTML page that needs it.

<script src="https://unpkg.com/hyperscript.org@0.9.12"></script>

Check the _hyperscript website to see if a newer version is available. Version 0.9.12 was released in October 2023.

_hyperscript cannot be installed from npm.

Underscore Attribute

The value of the _ attribute is a string of _hyperscript code. The name script or data-script can be used in place of _, but those names are not commonly used.

Features

Each HTML element can only have one underscore attribute.

Each underscore attribute can describe one or more features.

The following example demonstrates using the init and on features, which are the most commonly used features.

The init feature specifies commands to be executed when the associated element is initialized.

The on feature lists the events that will cause the commands that follow to be executed. When there are multiple event names, they are separated by the or keyword.

The log command, by default, uses the console.log function to write the DevTools Console.

<html>
<head>
<script src="https://unpkg.com/hyperscript.org@0.9.12"></script>
</head>
<body>
See output in the DevTools console.

<!-- This demonstrates defining multiple features in one _ value. -->
<div
_="
init log 'initialized'
on click log 'got click'
on mouseover log 'got mouseover'
"

>

Move the mouse over me and click me to execute the features.
</div>
</body>
</html>

Other supported features include:

Variables

Hyperscript can access JavaScript variables that are declared with the var keyword, but not those declared with the const or let keywords.

Hyperscript can declare and use its own variables. These have three scopes.

Global variables can be used in any _hyperscript command. There are two ways to create a global variable. Either there names must begin with a dollar sign or they must be set with the global keyword.

Element variables are scoped to an element, but can be accessed in any of its features. Their names must start with a colon.

All other variables are local and can only be used in the feature in which they are set.

The following example demonstrates defining and using all three variable scopes.

<html>
<head>
<script src="https://unpkg.com/hyperscript.org@0.9.12"></script>
<script>
// _hyperscript cannot access variables declared with const or let.
var j = 1;
</script>
</head>
<body>
See output in the DevTools console.

<!-- This demonstates accessing a JavaScript variable. -->
<div _="init log 'j =', j">logged JS variable</div>

<!-- These demonstrate defining a global-scoped variable
and accessing it in multiple elements. -->

<div _="init set global g to 3">set g</div>
<div _="init log '$g =', $g">logged g</div>
<div _="init set my.textContent to g"></div>

<!-- This demonstrates setting an element-scoped variable
and accessing it in other features. -->

<div
_="
init set :e to 4 then log 'init :e =', :e
on click log 'click :e =', :e -- can access because it's element-scoped
on mouseover log 'mouseover :e =', :e
"

>

mouseover and click for element-scoped variable
</div>

<!-- This demonstrates setting a local-scoped variable
and not being able to access it in other features. -->

<div
_="
init set l to 5 then log 'init l =', l
on click log 'click l =', l -- undefined because it's local-scoped
"

>

click for local-scoped variable
</div>
</body>
</html>

Commands

_hyperscript supports a large number of commands and keywords.

Each _hyperscript command is described in the following table.

CommandDescription
addadds an attribute, CSS class, or CSS property to an element
appendappends a string to another string, a value to an array, or element to another element
asyncexecutes a command or block of commmands asynchronously
beep!prints the source, result, and type of an expression to the console
breakexits a repeat loop
callevaluates an expression and places the result in the it variable
continuecontinues to the next iteration of a repeat loop
decrementdecrements a variable, property, or attribute (see by keyword)
defaultsets the default value of a variable or property
exitexits a function or event handler without returning a value
fetchfetches text, JSON, HTML, or raw data from an HTTP endpoint and places the result in the it variable
foriterates over items in an expression; specifies target of take
fromspecifies source of take
getan alias for call, used when it reads better
gonavigates to a URL, back to the previous page, or scrolls an element into view
haltprevents an event from bubbling
hidehides an element by changing its CSS display, visibility, or opacity property
ifprovides conditional control flow
incrementincrements a variable, property, or attribute (see by keyword)
jsembeds JavaScript code; terminated by end
logwrites using console.log unless another variant is specified after with keyword
makecreates an instance of a DOM class (an element)
measuregets measurements from an element
onspecifies events (separated by or) that trigger the commands that follow
pickgets array elements using the slice method
putinserts content into a variable, property, or the DOM
removeremoves an element from the DOM or a class/property from an element
renderclones a template element and populates it; result goes in result and it
repeatiterates over items in an expression, a number of times, or forever
returnreturns a value from a function or exits from an event handler
sendsends an event to a target element
setsets a variable or element property
settlesynchronizes on a CSS transition of an element
showshows an element by changing its CSS display, visibility, or opacity property
takeremoves a class or attribute from elements and adds it to another element
telltemporarily changes the default target for a command
throwthrows an exception
toggletoggles CSS classes, an attribute, or the visibility of an element
transitiontransitions properties on an element from one value to another
triggeralias for send
waitblocks until an event occurs or for a given amount of time

Keywords

Each _hyperscript keyword is described in the following table. Many of the keywords are used in "pseudo-commands" that treat an object method as a top-level command.

KeywordDescription
andused in logical expressions
atused in pseudo-commands
backmodifier for the go command
bottomindicates a relative position
bymodifier for the decrement and increment commands which default to 1
centerindicates a relative position
characterspecifies getting a single character with the pick command
charactersspecifies getting multiple characters with the pick command
dobegins a block of commmands
elseoptionally used with if
emptycomparison value
endends a block of commmands
forused in repeat commands
foreverused in repeat commands
fromused in pseudo-commands
inused in repeat commands
intoused in pseudo-commands
iscomparison operator
itemspecifies the kind of result to get with the pick command
itemsspecifies the kind of result to get with the pick command
itspossessive that refers to another element
leftindicates a relative position
matchspecifies getting a regular expression match with the pick command
matchesspecifies getting a regular expression matches with the pick command
mepossessive that refers to the current element
middleindicates a relative position
myalias for me
nextfinds the next element of a given type
notused in logical expressions
ofmakes commands more readable
onused in pseudo-commands
orused in logical expressions
otherwisealias for else; used with if
previousfinds the previous element of a given type
rightindicates a relative position
themakes commands more readable
thenseparates multiple commands; optionally used with if
timesindicates the number of times a repeat block will execute
toused with append, go, and pseudo-commands
topindicates a relative position
untilused in repeat commands
whileused in repeat commands
withspecifies the console method that log should use; also used in pseudo-commands

Examples

Let's walk through some examples of using _hyperscript to add interactivity in web applications.

Conditional Visibility

The following code renders a button that toggles whether a div is visible. Note the readability of the _hyperscript code on the button element.

_hyperscript visibility toggle

<html>
<head>
<script src="https://unpkg.com/hyperscript.org@0.9.12"></script>
<style>
.message {
font-size: 3rem;
opacity: 0;
transition: opacity 1s;
}
</style>
</head>
<body>
<div>
<button _="on click toggle the *opacity of the next <div/>">
Toggle
</button>
<div class="message">Hello, World!</div>
</div>
</body>
</html>

Counter

The following code implements a basic counter component. Note that the if command doesn't require the end keyword if it is the last command.

_hyperscript counter

<html>
<head>
<script src="https://unpkg.com/hyperscript.org@0.9.12"></script>
</head>
<body>
<div style="display: flex; gap: 1rem">
<button
disabled
_="on click
remove @disabled from the next <button/>
decrement the textContent of #count
if it is 0 then add @disabled to me
"

>

-
</button>
<span id="count">0</span>
<button
_="on click
remove @disabled from the previous <button/>
increment the textContent of #count
if it is 10 then add @disabled to me
"

>

+
</button>
</div>
</body>
</html>

Using x-for and x-if Directives

The following example demonstrates using the for and if commands. It also demonstrates using the make, set, and put commands to make a DOM element, set its properties, and put it into the DOM.

_hyperscript for and if

<html>
<head>
<script src="https://unpkg.com/hyperscript.org@0.9.12"></script>
<script>
// _hyperscript cannot access variables declared with const or let.
var colors = [
{name: 'blue', primary: true},
{name: 'green', primary: false},
{name: 'orange', primary: false},
{name: 'purple', primary: false},
{name: 'red', primary: true},
{name: 'yellow', primary: true}
];
</script>
</head>
<body style="background-color: gray">
<h1>Primary Colors</h1>
<div
_="init
for color in colors
if color.primary
make a <div/>
-- The * indicates that we are setting a CSS property.
set its *color to color.name
set its *fontSize to 2rem
set its textContent to color.name
put it at the end of me
end
end
"

>
</div>
</body>
</html>

Score Keeper

This example demonstrates several more _hyperscript features.

The CSS is the same as in the Alpine version.

_hyperscript Score Keeper

Here is the HTML.

<html>
<head>
<title>_hyperscript Score Keeper</title>
<link rel="stylesheet" href="score-keeper.css" />
<script src="https://unpkg.com/hyperscript.org@0.9.12"></script>
<script type="text/hyperscript">
def color(team)
if team.like return 'red' end
return 'white'
end

def heart(team)
if team.like return '{red-heart}️' end
return '{white-heart}'
-- Replace `{red-heart}` and `{white-heart}` above
-- with the corresponding emojis.
end

def report()
if the score of $team1 is greater than the score of $team2
set text to `The ${$team1.name} are winning.`
else if the score of $team2 is greater than the score of $team1
set text to `The ${$team2.name} are winning.`
else
set text to 'The score is tied.'
end
set the textContent of #report to text
end
</script>
</head>
<body
_="init
set $team1 to {name: 'Chiefs', like: false, score: 25}
set $team2 to {name: '49ers', like: false, score: 22}
"

>

<main class="column">
<div id="report" _="init report()"></div>
<section class="column team">
<label>
Team
<input
type="text"
_="
init set my value to $team1.name
on change set $team1.name to my value then report()
"

/>

</label>
<label>
Score
<input
type="number"
_="
init set my value to $team1.score
on change set $team1.score to my value then report()
"

/>

</label>
<button
_="
init set my textContent to heart($team1)
on click
set $team1.like to not $team1.like
set *border-color of closest <section/> to color($team1)
set my textContent to heart($team1)
"

>
</button>
</section>
<section class="column team">
<label>
Team
<input
type="text"
_="
init set my value to $team2.name
on change set $team2.name to my value then report()
"

/>

</label>
<label>
Score
<input
type="number"
_="
init set my value to $team2.score
on change set $team2.score to my value then report()
"

/>

</label>
<button
_="
init set my textContent to heart($team2)
on click
set $team2.like to not $team2.like
set *border-color of closest <section/> to color($team2)
set my textContent to heart($team2)
"

>
</button>
</section>
</main>
</body>
</html>

DOM Literals

_hyperscript supports syntax for referring to parts of the DOM. The following table describes this syntax.

DOM TargetSyntax
element by id#someID
elements by CSS class.someClassName
elements by CSS selector<someCSSSelector/>
attribute value@someAttributeName
CSS property value*someCSSProperty

The following code demonstrates using each kind of DOM literal.

<html>
<head>
<script src="https://unpkg.com/hyperscript.org@0.9.12"></script>
<style>
.styled {
border: 1px solid gray;
border-radius: 0.5rem;
padding: 0.5rem;
}
</style>
</head>
<body
_="init
set el to #my-id -- finds an element by its id
log el

set id to @id of el -- gets an element attribute value
log '@id =', id

for el in .my-class -- finds elements by a CSS class
log el
end

for btn in <button/> -- finds elements by tag name
remove @disabled from btn -- removes an attribute
add .styled to btn -- adds a CSS class to the element
end

set the *color of #d4 to 'blue' -- sets a CSS property
-- Alternative using 's.
-- set #d4's *color to 'blue'
"

>

<div id="my-id">One</div>
<div class="my-class">Two</div>
<div class="my-class">Three</div>
<button disabled _="on click remove me">Click Me</button>
<div id="d4" style="color: red">Four</div>
</body>
</html>

Errors

When syntax errors are encountered, error messages are written to the DevTools Console. They are quite descriptive.

Comments

Single-line comments begin with -- followed by at least one whitespace character.

For now, // for single-line comments and /* ... */ for multi-line comments are also supported, but those may not be supported in the future.

Attributes

To get the value of an attribute on the current element, use @attr-name. The value will always be a string.

To set the value of an attribute on the current element, use set @attr-name to {value}. If the value is not a string, it will be converted to a string.

Strings

Literal strings are delimited with single or double quotes. Strings can be concatenated with the + operator. Interpolation is performed in the same way as in JavaScript. For example:

set fullName to `${firstName} ${lastName}`

Arrays

The following code demonstrates operating on arrays.

set scores to [7, 19, 12]
log the first of scores -- 7
log the last of scores -- 12
log random in scores -- one of the values

Objects

Objects are created with the same literal syntax as in JavaScript. For example:

set dog to {name: "Comet", breed: "Whippet"}
log `${dog.name} is a ${dog.breed}`

There are multiple ways to access a property of an object.

DOM Access

Possessive expressions can be used to get and set DOM content. For example:

get the first 
  • then
    set my innerHTML to its innerHTML
  • Operators

    Comparison Operators

    _hyperscript supports all the comparison operators in JavaScript. It also supports many comparison keywords.

    Other operators with no direct comparison to JavaScript include the following:

    Math Operators

    _hyperscript supports most JavaScript math operators. It does not support the % operator for modulo, but the mod keyword can be used instead.

    _hyperscript evaluates all operators from left to right and doesn't apply operator precedence. Parentheses must be used when an expression uses multiple operators.

    The increment and decrement keywords modify the value of a number as their name implies.

    Conversions

    To convert a string to a number, follow it with as an Int.

    Conditional Logic

    if {condition}
    ...
    end

    Iteration

    _hyperscript supports many kinds of loops. The break and continue keywords can be used in all of them.

    for {var} in {collection}
    ...
    end

    for {var} in {collection} index {i}
    ...
    end

    repeat forever
    ...
    end

    repeat in {collection} -- sets the "it" variable
    ...
    end

    repeat while {condition}
    ...
    end

    repeat until {condition}
    ...
    end

    repeat {n} times
    ...
    end

    Some commands automatically operate on all items in a collection. For example, add .some-class to <li/> adds the CSS class some-class to all li elements.

    Resources

    TODO

    Organize this content:

    can combine with tailwind in order to use provided classes like hidden. how good are the error messages if there is something wrong in the syntax? Do error messages appear in the Dev tools console? Do error messages appear on page load or only when hyperscript is triggered? see the control flow section in the docs for conditional logic and iteration. maybe Alpine is better for this because it allows using JavaScript syntax. The then keyword allows specifying a sequence of hyperscript statements. what is the difference between if else and if otherwise?

    on click toggle .some-class on selector click is an event. toggle is an action

    List all the supported events. click, load, … List all the supported keywords and describe each one. toggle, transition, on, my, over to, seconds, then, … is there a keyword that runs two things at once unlike the then keyword that runs them serially? selectors can use the keyword me.

    make sure you understand what the defer attribute does on a script tag. resetting a form can be done in a single attribute with hyper script, but requires three things with Alpine, x-data, x-on, and an id on the form.