from fasthtml.common import *
from fasthtml.jupyter import *
from fhdaisy import *fhdaisy
fhdaisy is a Python wrapper for DaisyUI that brings its component classes to FastHTML applications. Instead of manually writing HTML elements with DaisyUI classes like <button class="btn btn-primary">, you use Python components like Btn('Click me', cls='-primary'). The library follows a consistent pattern: every DaisyUI CSS class becomes a title-case Python component (e.g., card → Card, alert → Alert), and modifiers can be shortened by dropping the redundant prefix (e.g., btn-primary → -primary). Beyond the standard components, fhdaisy.xtras provides helper functions for common patterns like accordions and forms, reducing repetitive code while maintaining the flexibility to create custom helpers for your specific needs.
Usage
Installation
Install latest from pypi
$ pip install fhdaisyDocumentation
DaisyUI
DaisyUI is a component library built on top of Tailwind CSS that provides semantic class names for common UI patterns. While Tailwind offers utility classes like bg-blue-500 or p-4, DaisyUI adds higher-level component classes like btn, card, and modal that encapsulate the many utility classes typically needed for these elements. This means you can create a styled button with class="btn btn-primary" instead of combining dozens of Tailwind utilities.
For FastHTML developers, DaisyUI is particularly useful because it provides a complete design system out of the box without requiring JavaScript frameworks. The components are purely CSS-based and work perfectly with FastHTML’s server-side rendering approach. You get professional-looking components with built-in themes, responsive design, and accessibility features, while maintaining the simplicity of working with HTML elements.
Daisy basics
Let’s look at a specific example of a DaisyUI component - the btn.
DaisyUI creates buttons by using a btn class, along with other special classes prefixed by btn-, eg:
c = Button('Hey there', cls='btn btn-primary')
print(c)<button class="btn btn-primary">Hey there</button>
Let’s take a look at the result. For creating accurate previews in Jupyter, Solveit, and similar environments, we provide mk_previewer():
p = mk_previewer()We can now use this to preview our button:
p(c)fhdaisy basics
Now let’s see how fhdaisy makes this simpler and more Pythonic. Instead of using generic HTML elements with DaisyUI classes, fhdaisy provides Python components that correspond directly to DaisyUI’s component classes. The key insight is that DaisyUI’s CSS class names already tell us what kind of component we’re creating - so why not use them as Python component names?
Here’s how it works: fhdaisy takes each DaisyUI base class (like btn, card, alert) and turns it into a Python component with a title-case name (Btn, Card, Alert). When you use Btn() in your code, fhdaisy automatically creates a button element with the btn class already applied.
While the component names in fhdaisy come from DaisyUI’s CSS classes, the underlying HTML elements are automatically chosen to match what DaisyUI requires. Each component knows its correct HTML tag - Btn creates a <button>, Alert creates a <div>, Input creates an <input>, and so on. This mapping follows DaisyUI’s own documentation and ensures your components work correctly with all of DaisyUI’s styling and behavior.
Additional convenience comes with modifiers. In DaisyUI, you style a button by adding classes like btn-primary, btn-outline, or btn-sm. Notice how they all repeat the btn- prefix? With fhdaisy, you can use a shorthand notation in the cls parameter: just write -primary, -outline, or -sm. The component knows it’s a Btn, so it automatically adds the btn- prefix back when generating the HTML. This means less typing, less repetition, and cleaner code that’s easier to read and maintain.
Let’s see this in action with the same btn as before:
c = Btn('Hey there', cls='-primary')
print(c)<button class="btn btn-primary ">Hey there</button>
This renders identically to the previous manual version.
p(c)Let’s use another example to look deeper into how fhdaisy’s API design perfectly mirrors DaisyUI’s class structure.
DaisyUI’s form inputs use the input CSS class. Just like with buttons, DaisyUI modifies input elements through special classes. For example: - input - base input styling - input-bordered - adds a border - input-primary - uses primary color theme
Here’s how you’d write a traditional DaisyUI input:
p( Input(placeholder='Enter name', cls='input input-bordered') )With fhdaisy, the pattern is consistent: use the title-case version of the DaisyUI class name as your component. So input becomes Input; fhdaisy automatically adds the base input class when you use the Input component. Any modifiers like input-bordered can be shortened to just -bordered in the cls parameter.
p( Input(placeholder='Enter name', cls='-bordered') )print( Alert('Success! Your changes have been saved', cls='-success -soft') )<div class="alert alert-success alert-soft ">Success! Your changes have been saved</div>
DaisyUI creates alerts using the alert class on a <div> element. Here’s the traditional DaisyUI approach:
p( Div('This is an important message!', cls='alert alert-info') )Notice that DaisyUI uses a <div> tag here, not an <alert> tag (which doesn’t exist in HTML). The styling comes entirely from the alert class.
With fhdaisy, you use the title-case version of the CSS class name as your component - so alert becomes Alert. It doesn’t matter that the underlying HTML tag is a div:
p( Alert('This is an important message!', cls='-info') )Multi-part components
Some DaisyUI components are more complex and contain multiple structural parts. For instance, cards are composed of several nested elements that work together:
- A
<div>with classcardas the container - A
<figure>element for images (optional) - A
<div>with classcard-bodyfor the main content - Inside the card body, you might have:
- A
<h2>with classcard-title - Content paragraphs
- A
<div>with classcard-actionsfor buttons
- A
With fhdaisy, each part of the component follows the same naming pattern we’ve already seen. The base card class becomes Card, and each part like card-body, card-title, and card-actions becomes CardBody, CardTitle, and CardActions respectively:
p ( Card(
Figure(Img(src='https://picsum.photos/seed/fd/400/225')),
CardBody(
CardTitle('Card title'),
P('This is a sample card with some content'),
CardActions(cls='justify-end')( Btn('Buy Now', cls='-primary') )
) , cls='w-96 bg-base-100 shadow-sm'
) )Xtras
from fhdaisy.xtras import *
import fasthtml.components as fhSome components have a very rigid structure, for instance an item in an accordian alway has these pieces:
p ( Collapse(
fh.Input(type='radio', name='acc1', checked="checked"),
CollapseTitle('Click to expand', cls='font-semibold'),
CollapseContent('This is the hidden content', cls='text-sm'),
cls='-arrow border border-base-300'
) )fhdaisy.xtras provides a number of functions to make these more concise. For instance for the above:
p (mk_accordion_item('Click to expand', 'This is the hidden content',
name='acc1', checked=True, cls='-arrow border border-base-300', titlecls='font-semibold'))Accordians contain a number of items, again in a specific format. There’s a function to simplify this too (all these “xtras” functions have the prefix mk_, to distinguish them from the components and parts that exactly map to DaisyUI’s standard syntax):
accitems = [
('How do I create an account?', 'Click the "Sign Up" button in the top right corner.'),
('I forgot my password', 'Click on "Forgot Password" on the login page.'),
('How do I update my profile?', 'Go to "My Account" settings and select "Edit Profile".')
]p( mk_accordion(*accitems,
titlecls='font-semibold', contentcls='text-sm',
itemcls='-arrow border border-base-300',
cls='-vertical min-w-md') )Custom functions
You can create custom helper functions for components that have repetitive patterns that fhdaisy.xtras hasn’t yet created a wrapper for. You should follow the mk_ prefix convention to distinguish them from standard components.
The rating component is a good example. A DaisyUI rating consists of multiple masked input elements, where each represents one star (or other shape). The traditional approach requires creating each mask input individually:
p( Rating(
Mask(cls='-star-2 bg-orange-400', checked=True, name='rating-demo'),
Mask(cls='-star-2 bg-orange-400', checked=True, name='rating-demo'),
Mask(cls='-star-2 bg-orange-400', checked=True, name='rating-demo'),
Mask(cls='-star-2 bg-orange-400', checked=False, name='rating-demo'),
Mask(cls='-star-2 bg-orange-400', checked=False, name='rating-demo'),
cls='-sm'
) )This is repetitive and error-prone. You can create your own mk_rating function to simplify this by generating all the mask inputs automatically:
def mk_rating(n, checked, nm=None, cls='', itemcls=''):
return Rating(*[Mask(cls=itemcls, checked=(i<checked), name=nm) for i in range(n)], cls=cls)Now you can create the same 5-star rating with 3 stars selected much more concisely:
p( mk_rating(5, 3, nm='rating-demo', cls='-sm', itemcls='-star-2 bg-orange-400') )This pattern can be applied to any component with repetitive structures - identify the pattern, create a mk_ function that generates the structure, and expose the key parameters that vary.
Full example
Here’s an example showing a number of components being used together. This example was auto-generated using Sonnet-3.5 running inside Solveit:
c = Div(
Card(
Figure(Img(src="https://picsum.photos/seed/42/400/250")),
CardBody(
H2("Mountain Adventure", cls="card-title"),
Flex(
Badge("New", cls="-primary"),
Badge("Featured", cls="-secondary -outline"),
Badge("Travel", cls="-accent -soft"),
cls="gap-2 mb-3"),
P("Discover breathtaking mountain trails and scenic vistas on this unforgettable journey."),
Flex(
Avatar(
Div(Img(src="https://picsum.photos/80/80", cls="rounded-full"), cls="w-10"),
cls="-online"),
Div(
Div("Alex Chen", cls="font-semibold"),
Div("2 hours ago", cls="text-sm opacity-50")),
cls="items-center gap-3 my-4"),
mk_rating(5, 3, nm='rating-demo', cls='-sm', itemcls='-star-2 bg-orange-400'),
Progress(value="75", max="100", cls="-primary -sm mt-3"),
CardActions(
Btn("Learn More", cls="-primary"),
Btn("Bookmark"),
cls="justify-end mt-4")
),
cls="w-96 bg-base-100 shadow-xl"),
cls="min-h-screen bg-base-200 flex items-center justify-center p-8"
)p(c)Next steps
The best way to learn fhdaisy is through interactive experimentation. Use Jupyter or Solveit to explore components in real-time - create a previewer with mk_previewer(), then try different component combinations and modifiers to see instant results. Start with simple components like Btn or Alert, experiment with different -primary, -outline, or -lg modifiers, then work your way up to complex multi-part components like cards and modals. This hands-on approach helps you quickly understand how DaisyUI’s styling system works with fhdaisy’s Python API.
This documentation page has a markdown version (click “commonmark” on the right) that serves as excellent context for LLMs. When you provide this markdown to AI assistants, they become quite effective at generating fhdaisy components and can help you quickly prototype interfaces or convert existing HTML designs to use fhdaisy’s cleaner syntax.
To explore all available components and their modifiers, check out the DaisyUI component documentation. For building complete FastHTML applications, see the FastHTML documentation.
If you create useful mk_ helper functions for repetitive patterns, consider contributing them back to fhdaisy. The pattern is simple: identify common component structures, create functions prefixed with mk_, and submit them to the fhdaisy repository. Your contributions can help make the library even more convenient for the community.