home / skills / psincraian / myfy / frontend-module

This skill helps you integrate server-side rendering with Tailwind, DaisyUI, and Vite in frontend modules for seamless templates and static asset handling.

npx playbooks add skill psincraian/myfy --skill frontend-module

Review the files below or copy the command above to add this skill to your agents.

Files (1)
SKILL.md
6.5 KB
---
name: frontend-module
description: myfy FrontendModule for server-side rendering with Jinja2, Tailwind 4, DaisyUI 5, and Vite. Use when working with FrontendModule, templates, render_template, static files, Tailwind CSS, or Vite HMR.
---

# FrontendModule - Server-Side Rendering

FrontendModule provides Jinja2 templates with Tailwind 4, DaisyUI 5, and Vite bundling.

## Quick Start

```python
from myfy.core import Application
from myfy.web import WebModule, route
from myfy.frontend import FrontendModule, render_template

app = Application()
app.add_module(WebModule())
app.add_module(FrontendModule(auto_init=True))  # Auto-scaffolds!

@route.get("/")
async def home() -> str:
    return render_template("home.html", title="Welcome")
```

## Configuration

Environment variables use the `MYFY_FRONTEND_` prefix:

| Variable | Default | Description |
|----------|---------|-------------|
| `MYFY_FRONTEND_ENVIRONMENT` | `development` | Environment mode |
| `MYFY_FRONTEND_ENABLE_VITE_DEV` | `True` | Start Vite dev server |
| `MYFY_FRONTEND_VITE_DEV_SERVER` | `http://localhost:3001` | Vite server URL |
| `MYFY_FRONTEND_STATIC_URL_PREFIX` | `/static` | Static files URL path |
| `MYFY_FRONTEND_CACHE_STATIC_ASSETS` | `True` | Enable static caching |
| `MYFY_FRONTEND_CACHE_MAX_AGE` | `31536000` | Cache max-age (1 year) |
| `MYFY_FRONTEND_SHOW_VITE_LOGS` | `False` | Show Vite console output |

## Module Options

```python
FrontendModule(
    templates_dir="frontend/templates",  # Template directory
    static_dir="frontend/static",        # Static files directory
    auto_init=True,                      # Auto-scaffold if missing
)
```

## Auto-Scaffolding

With `auto_init=True`, FrontendModule creates:

```
frontend/
  templates/
    base.html           # Base template with Tailwind
    home.html           # Example home page
  static/
    src/
      main.js           # Entry point
      main.css          # Tailwind imports
package.json            # Node dependencies
vite.config.js          # Vite configuration
tailwind.config.js      # Tailwind configuration
```

## Template Rendering

### Basic Rendering

```python
from myfy.frontend import render_template

@route.get("/")
async def home() -> str:
    return render_template("home.html", title="Home")
```

### With Context

```python
@route.get("/users/{user_id}")
async def user_profile(user_id: int, session: AsyncSession) -> str:
    user = await session.get(User, user_id)
    return render_template("profile.html", user=user)
```

### With Request Context

```python
from starlette.requests import Request

@route.get("/dashboard")
async def dashboard(request: Request, user: User) -> str:
    return render_template(
        "dashboard.html",
        request=request,  # For url_for, CSRF, etc.
        user=user,
    )
```

## Base Template

```html
<!-- frontend/templates/base.html -->
<!DOCTYPE html>
<html lang="en" data-theme="light">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>{% block title %}My App{% endblock %}</title>
    {{ vite_assets("src/main.js") }}
</head>
<body class="min-h-screen bg-base-100">
    <div class="navbar bg-base-200">
        <a class="btn btn-ghost text-xl" href="/">My App</a>
    </div>
    <main class="container mx-auto px-4 py-8">
        {% block content %}{% endblock %}
    </main>
</body>
</html>
```

## Page Template

```html
<!-- frontend/templates/home.html -->
{% extends "base.html" %}

{% block title %}{{ title }} - My App{% endblock %}

{% block content %}
<div class="hero min-h-[50vh]">
    <div class="hero-content text-center">
        <div class="max-w-md">
            <h1 class="text-5xl font-bold">Hello, World!</h1>
            <p class="py-6">Welcome to your myfy application.</p>
            <button class="btn btn-primary">Get Started</button>
        </div>
    </div>
</div>
{% endblock %}
```

## DaisyUI Components

DaisyUI 5 provides ready-to-use components:

```html
<!-- Buttons -->
<button class="btn btn-primary">Primary</button>
<button class="btn btn-secondary">Secondary</button>
<button class="btn btn-outline">Outline</button>

<!-- Cards -->
<div class="card bg-base-100 shadow-xl">
    <div class="card-body">
        <h2 class="card-title">Card Title</h2>
        <p>Card content here.</p>
        <div class="card-actions justify-end">
            <button class="btn btn-primary">Action</button>
        </div>
    </div>
</div>

<!-- Forms -->
<div class="form-control">
    <label class="label">
        <span class="label-text">Email</span>
    </label>
    <input type="email" class="input input-bordered" />
</div>

<!-- Alerts -->
<div class="alert alert-success">
    <span>Success! Your action was completed.</span>
</div>
```

## Theme Switching

```html
<!-- Theme toggle -->
<label class="swap swap-rotate">
    <input type="checkbox" class="theme-controller" value="dark" />
    <svg class="swap-on w-6 h-6" fill="currentColor"><!-- sun icon --></svg>
    <svg class="swap-off w-6 h-6" fill="currentColor"><!-- moon icon --></svg>
</label>
```

## Static Assets

### Vite Helper

```html
<!-- Loads JS and CSS with HMR in development -->
{{ vite_assets("src/main.js") }}
```

### Manual Asset URLs

```html
<img src="{{ asset_url('images/logo.png') }}" alt="Logo">
```

## Development Workflow

1. Start the application:
   ```bash
   myfy run
   ```
   Vite dev server starts automatically with HMR.

2. Edit templates and CSS - changes reflect instantly.

3. Build for production:
   ```bash
   npm run build
   ```
   Creates optimized assets in `frontend/static/dist/`.

## Production Build

```bash
# Build assets
npm run build

# Set production mode
export MYFY_FRONTEND_ENVIRONMENT=production
export MYFY_FRONTEND_ENABLE_VITE_DEV=false

# Run application
myfy run
```

Assets are served from the built manifest with cache headers.

## Custom Jinja Filters

```python
from myfy.frontend import FrontendModule
from starlette.templating import Jinja2Templates

# After module initialization
templates: Jinja2Templates = container.get(Jinja2Templates)
templates.env.filters["currency"] = lambda x: f"${x:.2f}"
```

Use in templates:

```html
<span>{{ price | currency }}</span>
```

## Best Practices

1. **Use auto_init for new projects** - Gets you started quickly
2. **Extend base.html** - Keep consistent layout
3. **Use DaisyUI components** - Pre-styled, accessible
4. **Enable caching in production** - Set long cache max-age
5. **Build assets before deploy** - Don't rely on Vite in production
6. **Use template inheritance** - Keep templates DRY
7. **Pass request for url_for** - Enables dynamic URL generation

Overview

This skill integrates server-side rendering with Jinja2, Tailwind CSS 4, DaisyUI 5, and Vite for fast development and production-ready static assets. It scaffolds a frontend layout, templates, and asset pipeline and exposes render_template and helper utilities for easy template rendering. Use it to combine Python backend rendering with modern frontend tooling and HMR.

How this skill works

The module provides a Jinja2 templates directory, static asset folder, and Vite integration. In development it can start a Vite dev server with HMR and inject vite_assets for JS/CSS; in production it serves built assets from a manifest with long cache headers. Templates are rendered via render_template and can receive request, user, and other context values.

When to use it

  • You need SSR with Jinja2 and modern Tailwind + DaisyUI styling.
  • You want Vite-powered development with hot module replacement.
  • You need a quick scaffolded frontend (templates, static, config).
  • You want to serve optimized, cacheable assets in production.
  • You need template helpers like vite_assets and asset_url.

Best practices

  • Enable auto_init for new projects to scaffold templates and configs.
  • Extend a single base.html to keep layout and head assets consistent.
  • Pass the request object to render_template when using url_for or CSRF.
  • Build assets (npm run build) and disable Vite dev server in production.
  • Enable long cache max-age for static assets and use hashed filenames.

Example use cases

  • Render a homepage with Tailwind and DaisyUI components using render_template('home.html').
  • Develop frontend with Vite HMR while running the Python backend locally.
  • Serve user-specific pages with request context to generate dynamic URLs.
  • Add custom Jinja filters (e.g., currency) after module initialization.
  • Deploy production app that serves optimized assets from frontend/static/dist.

FAQ

How do I enable Vite dev server?

Set MYFY_FRONTEND_ENABLE_VITE_DEV=true (default true) and ensure Vite is available at MYFY_FRONTEND_VITE_DEV_SERVER.

How are assets served in production?

Run npm run build to create dist assets; set MYFY_FRONTEND_ENVIRONMENT=production and MYFY_FRONTEND_ENABLE_VITE_DEV=false so the module serves built assets from the manifest with cache headers.