Slug
Auto-populate a URL slug field from a title or name field as the user types.
Usage
Copy slug_controller.js to app/javascript/controllers/ and register it:
// app/javascript/controllers/index.js
import SlugController from "./slug_controller";
application.register("slug", SlugController);
HTML
<div data-controller="slug">
<div>
<label for="post_title">Title</label>
<input
id="post_title"
type="text"
name="post[title]"
data-slug-target="source"
data-action="input->slug#generate"
/>
</div>
<div>
<label for="post_slug">Slug</label>
<input
id="post_slug"
type="text"
name="post[slug]"
data-slug-target="output"
data-action="input->slug#lock"
/>
</div>
</div>
Once the user manually edits the output field, auto-generation stops. If the output already has a value when the controller connects (e.g. on an edit form), it starts locked. The locked state is stored in a data-slug-locked-value attribute on the controller element, so it survives controller reconnects (e.g. Turbo morphing).
Note: Only Latin-based characters are supported. Non-Latin scripts (CJK, Arabic, Hebrew, emoji, etc.) are stripped entirely and produce an empty slug. If your titles may contain non-Latin text, apply a server-side transliteration step before or after the slug is submitted.
API
Targets
| Target | Required | Description |
|---|---|---|
source | Yes | The field whose value is transformed into a slug. |
output | Yes | The field that receives the generated slug. |
Values
| Value | Type | Default | Description |
|---|---|---|---|
locked | Boolean | false | When true, auto-generation is disabled. Stored as data-slug-locked-value on the controller element; survives controller reconnects. Set by the controller on connect (if the output is pre-filled) or when the user edits the output field. |
Actions
| Action | Description |
|---|---|
generate | Transforms the source field value to a slug and writes it to the output field. No-op if the output has been manually edited. Wire to input on the source field. |
lock | Stops further auto-generation. Wire to input on the output field. |
Accessibility
The slug field is a standard text input — no additional ARIA attributes are required. Ensure both fields have visible <label> elements.