Weave Code
Weaver
Helps Laravel developers discover, compare, and choose open-source packages. See popularity, security, maintainers, and scores at a glance to make better decisions.
Feedback
Share your thoughts, report bugs, or suggest improvements.
Subject
Message
Twig Bundle
symkit/faq-bundle
cursor
twig
symfony-ux
live-components
i18n
translations
security
stimulus

Twig component, Live Component, and template conventions for Symfony bundles

Install
php artisan boost:add-skill symkit/faq-bundle

Save this content to: .cursor/rules/twig-bundle.mdc

# Twig & Live Component Conventions

## Twig Helpers (Mandatory)

Always use Twig helpers — never manual `data-*` attributes (XSS protection, refactor-safe):

```twig
{{ stimulus_controller('vendor--bundle--name', { url: path('route') }) }}
{{ stimulus_target('vendor--bundle--name', 'input') }}
{{ stimulus_action('vendor--bundle--name', 'actionName', 'input') }}
```

## Internationalization (i18n)

**Every user-facing string must be translated** — no hardcoded text anywhere.

- Use `{% trans_default_domain 'VendorNameBundle' %}` at the top of base templates to avoid repeating the domain
- Child templates inherit the domain from `{% extends %}`
- XLIFF files in `translations/` at bundle root (auto-discovered)
- Translation keys: `error.404.heading`, `error.action.home` — dot-separated, descriptive
- Always provide at least 2 locales (e.g. `fr` + `en`)
- Dynamic locale: `lang="{{ app.request.locale|default('en') }}"` on `<html>`
- **Extract all strings**: Twig (`{% trans %}`, `|trans`), form labels/help/placeholders, constraint messages, controller flash messages, email subjects/bodies, error pages
- Form labels: always explicit `'label' => 'form.field_name'`, never rely on auto-generated labels
- Validator messages: always use translation keys (`'message' => 'validation.field.constraint'`)
- Run `translation:extract` regularly to catch missing keys

## Self-Contained Pages (Error Pages, Emails)

For pages that must render independently of the application's asset pipeline:

- **Inline CSS only** — never CDN (`cdn.tailwindcss.com`, `unpkg.com`, etc.)
- **Inline SVG** for icons — never external icon libraries via `<script>`
- **No bare `<script src="">`** — pages must work offline / behind strict CSP
- CSS custom properties (`--var`) for theme values overridable by child templates
- Use semantic CSS class names (`.terminal-header`, not `.bg-subtle .px-4 .py-3`)

## Security in Templates

- **Never expose** `app.request.clientIp` (information leak)
- **Never display** fake trace IDs with `random()` (misleading)
- `app.request.pathInfo` is auto-escaped by Twig but still reveals internal paths — use with caution

## PHP → JS Data Transfer

Symfony Serializer with groups. Never `json_encode()` on objects:

```php
$data = $this->serializer->normalize($entity, 'json', ['groups' => 'entity:read']);
```

## Twig Component vs Live Component

- No reactivity needed → Twig Component (`final class`, registered via `twig_component.defaults` in `prependExtension()`)
- Server-side reactivity → Live Component (`final class` + `DefaultActionTrait`, registered via `twig_component.defaults` in `prependExtension()`)
- **No `#[AsTwigComponent]` / `#[AsLiveComponent]` attributes in bundle code** — registration via config only (see symfony-bundle rule)

## Component Rules

- `{{ attributes }}` mandatory on root element of every component
- Prefix CSS classes with bundle name (`acme-blog-`)
- `{% props %}` for UI-only components (no PHP class)
- Namespace: `@VendorName`, support host app override

## Live Component Security

```
#[LiveProp]                              → read-only (server-controlled)
#[LiveProp(writable: true)]              → scalars ONLY
#[LiveProp(writable: ['field1'])]        → entities with granular fields
NEVER #[LiveProp(writable: true)] on entire entity (ID manipulation attack)
```

## Live Component Patterns

- `debounce(300)` or `lazy` on text inputs
- Stable `id` mandatory on iterated elements in `{% for %}` loops (DOM morphing)
- `LiveCollectionType` for dynamic forms (no JS prototype manipulation)
- Communication: `$this->emit('eventName', [...])` + `#[LiveListener('eventName')]`
- No business logic in components — delegate to services

## FORBIDDEN

- CDN links in self-contained templates (error pages, emails)
- Twig macros when a Twig Component does the same (components are typed, injectable)
- `ux-lazy-image` on LCP image (SEO penalty)
- `json_encode()` for objects in templates
- Exposing `app.request.clientIp` in user-facing templates

Twig & Live Component Conventions

Twig Helpers (Mandatory)

Always use Twig helpers — never manual data-* attributes (XSS protection, refactor-safe):

{{ stimulus_controller('vendor--bundle--name', { url: path('route') }) }}
{{ stimulus_target('vendor--bundle--name', 'input') }}
{{ stimulus_action('vendor--bundle--name', 'actionName', 'input') }}

Internationalization (i18n)

Every user-facing string must be translated — no hardcoded text anywhere.

  • Use {% trans_default_domain 'VendorNameBundle' %} at the top of base templates to avoid repeating the domain
  • Child templates inherit the domain from {% extends %}
  • XLIFF files in translations/ at bundle root (auto-discovered)
  • Translation keys: error.404.heading, error.action.home — dot-separated, descriptive
  • Always provide at least 2 locales (e.g. fr + en)
  • Dynamic locale: lang="{{ app.request.locale|default('en') }}" on <html>
  • Extract all strings: Twig ({% trans %}, |trans), form labels/help/placeholders, constraint messages, controller flash messages, email subjects/bodies, error pages
  • Form labels: always explicit 'label' => 'form.field_name', never rely on auto-generated labels
  • Validator messages: always use translation keys ('message' => 'validation.field.constraint')
  • Run translation:extract regularly to catch missing keys

Self-Contained Pages (Error Pages, Emails)

For pages that must render independently of the application's asset pipeline:

  • Inline CSS only — never CDN (cdn.tailwindcss.com, unpkg.com, etc.)
  • Inline SVG for icons — never external icon libraries via <script>
  • No bare <script src=""> — pages must work offline / behind strict CSP
  • CSS custom properties (--var) for theme values overridable by child templates
  • Use semantic CSS class names (.terminal-header, not .bg-subtle .px-4 .py-3)

Security in Templates

  • Never expose app.request.clientIp (information leak)
  • Never display fake trace IDs with random() (misleading)
  • app.request.pathInfo is auto-escaped by Twig but still reveals internal paths — use with caution

PHP → JS Data Transfer

Symfony Serializer with groups. Never json_encode() on objects:

$data = $this->serializer->normalize($entity, 'json', ['groups' => 'entity:read']);

Twig Component vs Live Component

  • No reactivity needed → Twig Component (final class, registered via twig_component.defaults in prependExtension())
  • Server-side reactivity → Live Component (final class + DefaultActionTrait, registered via twig_component.defaults in prependExtension())
  • No #[AsTwigComponent] / #[AsLiveComponent] attributes in bundle code — registration via config only (see symfony-bundle rule)

Component Rules

  • {{ attributes }} mandatory on root element of every component
  • Prefix CSS classes with bundle name (acme-blog-)
  • {% props %} for UI-only components (no PHP class)
  • Namespace: @VendorName, support host app override

Live Component Security

#[LiveProp]                              → read-only (server-controlled)
#[LiveProp(writable: true)]              → scalars ONLY
#[LiveProp(writable: ['field1'])]        → entities with granular fields
NEVER #[LiveProp(writable: true)] on entire entity (ID manipulation attack)

Live Component Patterns

  • debounce(300) or lazy on text inputs
  • Stable id mandatory on iterated elements in {% for %} loops (DOM morphing)
  • LiveCollectionType for dynamic forms (no JS prototype manipulation)
  • Communication: $this->emit('eventName', [...]) + #[LiveListener('eventName')]
  • No business logic in components — delegate to services

FORBIDDEN

  • CDN links in self-contained templates (error pages, emails)
  • Twig macros when a Twig Component does the same (components are typed, injectable)
  • ux-lazy-image on LCP image (SEO penalty)
  • json_encode() for objects in templates
  • Exposing app.request.clientIp in user-facing templates
Weaver

How can I help you explore Laravel packages today?

Conversation history is not saved when not logged in.
Prompt
Add packages to context
No packages found.
uni-method/symfony-lib
tacticmedia/scim-server-bundle
neika-ai/symfony-bundle
mattleads/agent-publishing-bundle
glavweb/framework-bundle
glavweb/extra-bundle
glavweb/api-bundle
glavweb/admin-bundle
adachsoft/api-version-bundle
elsayed85/laravel-api-exceptions
ouhssini/laravel-demo-mode
daworks/ncloud-outbound-mailer-driver
gp247/cms
ignibyte/aic-gates
nexa-merchant/blog
novasa/laravel-language-center
opencage/geocode
adolfoholzer/parameters
livenetworks/ln-starter
cgv/localized-resource