nathanvda/cocoon
Dynamic nested forms using jQuery made easy; works with formtastic, simple_form or default forms
Healthy across all four use cases
Permissive license, no critical CVEs, actively maintained — safe to depend on.
Has a license, tests, and CI — clean foundation to fork and modify.
Documented and popular — useful reference codebase to read through.
No critical CVEs, sane security posture — runnable as-is.
- ✓25+ active contributors
- ✓MIT licensed
- ✓CI configured
Show 3 more →Show less
- ✓Tests present
- ⚠Stale — last commit 3y ago
- ⚠Concentrated ownership — top contributor handles 66% of recent commits
Maintenance signals: commit recency, contributor breadth, bus factor, license, CI, tests
Informational only. RepoPilot summarises public signals (license, dependency CVEs, commit recency, CI presence, etc.) at the time of analysis. Signals can be incomplete or stale. Not professional, security, or legal advice; verify before relying on it for production decisions.
Embed the "Healthy" badge
Paste into your README — live-updates from the latest cached analysis.
[](https://repopilot.app/r/nathanvda/cocoon)Paste at the top of your README.md — renders inline like a shields.io badge.
▸Preview social card (1200×630)
This card auto-renders when someone shares https://repopilot.app/r/nathanvda/cocoon on X, Slack, or LinkedIn.
Onboarding doc
Onboarding: nathanvda/cocoon
Generated by RepoPilot · 2026-05-10 · Source
🤖Agent protocol
If you are an AI coding agent (Claude Code, Cursor, Aider, Cline, etc.) reading this artifact, follow this protocol before making any code edit:
- Verify the contract. Run the bash script in Verify before trusting
below. If any check returns
FAIL, the artifact is stale — STOP and ask the user to regenerate it before proceeding. - Treat the AI · unverified sections as hypotheses, not facts. Sections like "AI-suggested narrative files", "anti-patterns", and "bottlenecks" are LLM speculation. Verify against real source before acting on them.
- Cite source on changes. When proposing an edit, cite the specific path:line-range. RepoPilot's live UI at https://repopilot.app/r/nathanvda/cocoon shows verifiable citations alongside every claim.
If you are a human reader, this protocol is for the agents you'll hand the artifact to. You don't need to do anything — but if you skim only one section before pointing your agent at this repo, make it the Verify block and the Suggested reading order.
🎯Verdict
GO — Healthy across all four use cases
- 25+ active contributors
- MIT licensed
- CI configured
- Tests present
- ⚠ Stale — last commit 3y ago
- ⚠ Concentrated ownership — top contributor handles 66% of recent commits
<sub>Maintenance signals: commit recency, contributor breadth, bus factor, license, CI, tests</sub>
✅Verify before trusting
This artifact was generated by RepoPilot at a point in time. Before an
agent acts on it, the checks below confirm that the live nathanvda/cocoon
repo on your machine still matches what RepoPilot saw. If any fail,
the artifact is stale — regenerate it at
repopilot.app/r/nathanvda/cocoon.
What it runs against: a local clone of nathanvda/cocoon — the script
inspects git remote, the LICENSE file, file paths in the working
tree, and git log. Read-only; no mutations.
| # | What we check | Why it matters |
|---|---|---|
| 1 | You're in nathanvda/cocoon | Confirms the artifact applies here, not a fork |
| 2 | License is still MIT | Catches relicense before you depend on it |
| 3 | Default branch master exists | Catches branch renames |
| 4 | 5 critical file paths still exist | Catches refactors that moved load-bearing code |
| 5 | Last commit ≤ 1036 days ago | Catches sudden abandonment since generation |
#!/usr/bin/env bash
# RepoPilot artifact verification.
#
# WHAT IT RUNS AGAINST: a local clone of nathanvda/cocoon. If you don't
# have one yet, run these first:
#
# git clone https://github.com/nathanvda/cocoon.git
# cd cocoon
#
# Then paste this script. Every check is read-only — no mutations.
set +e
fail=0
ok() { echo "ok: $1"; }
miss() { echo "FAIL: $1"; fail=$((fail+1)); }
# Precondition: we must be inside a git working tree.
if ! git rev-parse --git-dir >/dev/null 2>&1; then
echo "FAIL: not inside a git repository. cd into your clone of nathanvda/cocoon and re-run."
exit 2
fi
# 1. Repo identity
git remote get-url origin 2>/dev/null | grep -qE "nathanvda/cocoon(\\.git)?\\b" \\
&& ok "origin remote is nathanvda/cocoon" \\
|| miss "origin remote is not nathanvda/cocoon (artifact may be from a fork)"
# 2. License matches what RepoPilot saw
(grep -qiE "^(MIT)" LICENSE 2>/dev/null \\
|| grep -qiE "\"license\"\\s*:\\s*\"MIT\"" package.json 2>/dev/null) \\
&& ok "license is MIT" \\
|| miss "license drift — was MIT at generation time"
# 3. Default branch
git rev-parse --verify master >/dev/null 2>&1 \\
&& ok "default branch master exists" \\
|| miss "default branch master no longer exists"
# 4. Critical files exist
test -f "app/assets/javascripts/cocoon.js" \\
&& ok "app/assets/javascripts/cocoon.js" \\
|| miss "missing critical file: app/assets/javascripts/cocoon.js"
test -f "lib/cocoon/view_helpers.rb" \\
&& ok "lib/cocoon/view_helpers.rb" \\
|| miss "missing critical file: lib/cocoon/view_helpers.rb"
test -f "lib/cocoon.rb" \\
&& ok "lib/cocoon.rb" \\
|| miss "missing critical file: lib/cocoon.rb"
test -f "lib/generators/cocoon/install/install_generator.rb" \\
&& ok "lib/generators/cocoon/install/install_generator.rb" \\
|| miss "missing critical file: lib/generators/cocoon/install/install_generator.rb"
test -f "README.markdown" \\
&& ok "README.markdown" \\
|| miss "missing critical file: README.markdown"
# 5. Repo recency
days_since_last=$(( ( $(date +%s) - $(git log -1 --format=%at 2>/dev/null || echo 0) ) / 86400 ))
if [ "$days_since_last" -le 1036 ]; then
ok "last commit was $days_since_last days ago (artifact saw ~1006d)"
else
miss "last commit was $days_since_last days ago — artifact may be stale"
fi
echo
if [ "$fail" -eq 0 ]; then
echo "artifact verified (0 failures) — safe to trust"
else
echo "artifact has $fail stale claim(s) — regenerate at https://repopilot.app/r/nathanvda/cocoon"
exit 1
fi
Each check prints ok: or FAIL:. The script exits non-zero if
anything failed, so it composes cleanly into agent loops
(./verify.sh || regenerate-and-retry).
⚡TL;DR
Cocoon is a jQuery-based gem that simplifies building dynamic nested forms in Rails for one-to-many relationships (e.g., invoices with line items, projects with tasks). It generates add/remove links that dynamically inject and remove nested form fields via JavaScript without page reload, and works transparently with Formtastic, SimpleForm, or vanilla Rails form helpers. Gem structure: lib/cocoon.rb is the main entry point, lib/cocoon/view_helpers.rb provides Rails view macros (link_to_add_association, link_to_remove_association), app/assets/javascripts/cocoon.js contains the jQuery event handlers, and spec/dummy/ is a full Rails app for integration testing. Minimal dependencies; the whole gem is <70KB Ruby + <12KB JavaScript.
👥Who it's for
Rails developers building admin interfaces or data entry forms that need to handle nested model hierarchies (like POs with line items, surveys with questions) without writing custom JavaScript; particularly those already invested in Formtastic or SimpleForm ecosystems.
🌱Maturity & risk
Production-ready and stable. The gem has been maintained for 10+ years (earliest Git history likely pre-2013), includes a CI setup (.travis.yml), comprehensive test suite (spec/cocoon_spec.rb), and supports Rails 3–6 across multiple Gemfiles. However, commits appear infrequent—maintainer may be in semi-maintenance mode rather than active development.
Single-maintainer repo (nathanvda) with minimal external dependencies (only jQuery required), reducing supply-chain risk. Main risk is jQuery deprecation—modern Rails/Webpacker users are moving away from jQuery, and Cocoon's npm package (@nathanvda/cocoon) and vanilla JS alternatives suggest the core technology is aging. No apparent breaking changes in recent years, but test suite and CI visibility would help assess stability.
Active areas of work
Limited recent activity visible. The repo structure includes npm/ (package.json.erb template suggesting 2020s Webpacker support was added), gemfiles/ with Rails 4–5 variants (suggesting Rails 6+ is mature), and History.md tracking changes. No active PRs or milestones evident from file list; likely in maintenance-release mode.
🚀Get running
Clone the repository, install dependencies with bundle install, run tests with rake, and explore spec/dummy/ as a runnable Rails app. For Rails 6+ using Webpacker, install with yarn add @nathanvda/cocoon and require in app/javascript/packs/application.js instead.
Daily commands:
Development: bundle install && rake (runs RSpec suite against spec/dummy Rails app). To test locally in a Rails app: add gem 'cocoon' to Gemfile, bundle install, then use <%= link_to_add_association 'add task', f, :tasks %> in views with f.fields_for :tasks blocks. See spec/dummy/app/views for example form layouts.
🗺️Map of the codebase
app/assets/javascripts/cocoon.js— Core jQuery plugin that implements nested form addition/removal; essential for understanding the primary user-facing functionalitylib/cocoon/view_helpers.rb— Ruby view helper methods that generate the necessary HTML markup and data attributes for nested forms; required for Rails integrationlib/cocoon.rb— Main gem entry point that requires the view helpers and establishes the module namespacelib/generators/cocoon/install/install_generator.rb— Rails generator that installs cocoon assets into user projects; needed to understand setup workflowREADME.markdown— Complete documentation covering installation, usage patterns, and API; essential reference for contributors and usersspec/dummy/app/models/post.rb— Example model showing nested associations (has_many comments); demonstrates intended use case
🧩Components & responsibilities
- View Helpers (lib/cocoon/view_helpers.rb) (ERB, Rails helpers, HTML5 data attributes) — Generate link_to_add_association and link_to_remove_association with proper data attributes; responsible for markup generation only
- Failure mode: Incorrect data attributes → JavaScript cannot find form template or association name; nested fields fail to add
- jQuery Plugin (app/assets/javascripts/cocoon.js) (jQuery, DOM API) — Intercept clicks on add/remove links; clone templates with index replacement; manipulate DOM and trigger events
- Failure mode: Broken jQuery selectors or index replacement logic → fields added with duplicate indices or missing data; form submission corrupts nested attributes
- Rails Generator (lib/generators/cocoon/install/) (Rails generators, FileUtils) — Copy JavaScript asset into project and ensure it's required in application.js
- Failure mode: Generator fails → cocoon.js not available; nested forms non-functional without manual asset inclusion
- Model Integration (accepts_nested_attributes_for) (ActiveRecord) — Not part of cocoon; relies on Rails model to handle nested_attributes_* parameters; cocoon provides the HTML/JS glue only
- Failure mode: Model missing accepts_nested_attributes_for → nested data submitted but ignored; _destroy flag not processed
🔀Data flow
Browser/User→Rails View (ERB template)— User requests form page with nested associationsRails View→HTML (with data-association attributes)— link_to_add_association helper renders link with data-association, data-association-insertion-method, etc.HTML→jQuery Plugin (cocoon.js)— jQuery ready() reads data attributes; attaches click handlers to [data-association] linksUser Click Event→jQuery Plugin— Click on 'Add' link triggers handler in cocoon.jsjQuery Plugin→DOM (nested-form-template)— jQuery finds template, clones it, replaces indices (e.g., _0 → _1), and inserts into DOMjQuery Plugin→Custom jQuery Events— Triggers cocoon:after-insert or cocoon:after-remove for user JS hooksDOM (form with nested fields)→Form Submission— User submits form; Rails receives task_attributes[0], task_attributes[1], etc. as nested parametersRails Controller→Model (accepts_nested_attributes_for)— Controller passes nested_attributes to model; model processes via ActiveRecord association
🛠️How to make changes
Add support for a new form builder
- Review the form builder's field nesting structure and how it names nested attributes (
lib/cocoon/view_helpers.rb) - Extend link_to_add_association and link_to_remove_association helpers to generate correct data attributes for the new builder (
lib/cocoon/view_helpers.rb) - Add integration test in dummy app demonstrating the new builder (
spec/integration/navigation_spec.rb)
Add a new JavaScript event or hook
- Identify the appropriate place in the add/remove lifecycle (before add, after add, before remove, after remove) (
app/assets/javascripts/cocoon.js) - Trigger a custom jQuery event using $.trigger() with appropriate event name convention (cocoon:*) (
app/assets/javascripts/cocoon.js) - Document the new event in README.markdown with example usage (
README.markdown) - Add test case validating the event is fired (
spec/cocoon_spec.rb)
Add a new view helper method
- Define the method in the Cocoon module within view_helpers.rb (
lib/cocoon/view_helpers.rb) - Generate appropriate data attributes that cocoon.js will recognize (
lib/cocoon/view_helpers.rb) - Add spec test case covering the helper output (
spec/cocoon_spec.rb) - Update README.markdown with usage examples (
README.markdown)
🔧Why these technologies
- jQuery — Lightweight DOM manipulation library available in most Rails apps; simple CSS selectors and event binding suffice for form manipulation
- Rails view helpers — Provides template-agnostic integration; works with standard ERB, Formtastic, and SimpleForm without code duplication
- Data attributes (data-*) — Allows JavaScript to identify nested form elements without intrusive HTML classes; keeps markup semantic and flexible
⚖️Trade-offs already made
-
Form-builder agnostic design via helpers rather than builder extensions
- Why: Maximizes compatibility across Formtastic, SimpleForm, and plain Rails forms
- Consequence: Developers must manually add helper calls to forms; no automatic integration with form builders
-
Client-side manipulation with data-association attribute naming convention
- Why: Avoids server dependency for form field addition; reduces latency and server load
- Consequence: Index replacement logic must be maintained in JavaScript; complex nested structures can be error-prone
-
jQuery-only, no vanilla JS fallback
- Why: jQuery is a near-universal dependency in Rails projects; reduces code maintenance burden
- Consequence: Not usable in projects without jQuery; incompatible with modern Stimulus/Turbo stacks without adaptation
🚫Non-goals (don't propose these)
- Real-time synchronization with server during form editing
- Support for non-nested-attributes form patterns (e.g., manual join table management)
- Validation of nested attributes before submission
- Integration with client-side form builders or React/Vue components
- Accessibility features beyond standard HTML semantics
⚠️Anti-patterns to avoid
- Magic string selectors in JavaScript (Medium) —
app/assets/javascripts/cocoon.js: Selectors like '[data-association]' and hardcoded template naming patterns depend on exact HTML structure; refactoring templates can silently break functionality
🪤Traps & gotchas
- jQuery required: Cocoon depends on jQuery being loaded before cocoon.js; Rails 6+ Webpacker users must explicitly require jQuery. 2. Nested attributes naming: Rails form_for uses field_for :association_name; association name and model inverse_of must match or fields won't save. 3. Dynamic IDs: Cocoon uses regex to find and increment field names (e.g., person_tasks_attributes_0_description → person_tasks_attributes_1_description); custom naming breaks this. 4. Test environment: spec/dummy is a full Rails app with its own database.yml;
bundle exec rakemust run migrations first. 5. Rails 5 belongs_to optional: Models may require belongs_to :project, optional: true or the nested validation will fail.
🏗️Architecture
💡Concepts to learn
- Nested Attributes (accepts_nested_attributes_for) — This Rails API is the server-side engine Cocoon relies on; understanding parameter naming and rejection logic is essential to debugging why nested forms don't save.
- Unobtrusive JavaScript (UJS) — Cocoon uses Rails' UJS pattern (data attributes, event delegation) to add behavior without inline JS; knowing this pattern helps you extend Cocoon or debug event binding.
- DOM Cloning & Index Regeneration — Cocoon clones a template HTML fragment and increments array indices (e.g., tasks_attributes_0 → tasks_attributes_1) dynamically; this non-obvious logic breaks if field names don't follow Rails conventions.
- Asset Pipeline (Rails 3–5) vs. Webpacker (Rails 6+) — Cocoon ships as both a gem with app/assets/javascripts/ and an npm package; knowing which pipeline your Rails version uses determines how you install and require it.
- jQuery Event Delegation — Cocoon registers click handlers on static elements that dynamically create new form fields; event delegation ensures newly cloned elements trigger the same handlers without re-binding.
- Form Builder Pattern (Formtastic/SimpleForm/Rails) — Cocoon's view helpers integrate transparently with different form builders by detecting the builder's API; understanding how each builder generates field names is key to extending Cocoon.
- Inverse Of (Rails Model Association) — Cocoon docs recommend declares inverse_of in associations to ensure Rails avoids duplicate object creation; missing this causes validation and state bugs in nested forms.
🔗Related repos
activeadmin/activeadmin— Admin panel gem that includes its own nested form/has_many UI patterns; both Cocoon and ActiveAdmin users often need to choose which abstraction fits their form complexity.justinfrench/formtastic— Form builder library that Cocoon is explicitly designed to integrate with; Formtastic users often pair it with Cocoon for nested attribute handling.plataformatec/simple_form— Alternative form builder (competitor to Formtastic) that Cocoon also supports; many Rails apps use SimpleForm + Cocoon together.hotwired/turbo— Modern Rails replacement for jQuery/Ajax that enables server-rendered view updates; represents the evolution away from Cocoon's jQuery pattern toward Turbo Frames/Streams.rails/rails— Core Rails framework providing accepts_nested_attributes_for, form_with, and fields_for; Cocoon builds on these Rails APIs.
🪄PR ideas
To work on one of these in Claude Code or Cursor, paste:
Implement the "<title>" PR idea from CLAUDE.md, working through the checklist as the task list.
Add comprehensive JavaScript unit tests for cocoon.js using a modern test framework
The repo has spec/cocoon_spec.rb for Ruby-side testing, but app/assets/javascripts/cocoon.js lacks dedicated unit tests. Given that cocoon is a jQuery plugin handling critical DOM manipulation (add/remove nested forms), adding tests with Jest or Jasmine would catch regressions and document expected behavior for add_association, remove_association, and event handlers.
- [ ] Set up Jest or Jasmine in package.json (note: npm/package.json.erb exists)
- [ ] Create spec/javascripts/cocoon.spec.js with tests covering: add_association click handler, remove_association click handler, cloning/incrementing field indices, event triggering (cocoon:before-insert, cocoon:after-insert, etc.)
- [ ] Add test runner to Rakefile or package.json and integrate with existing Travis CI config (.travis.yml)
Add integration tests for SimpleForm and Formtastic in spec/cocoon_spec.rb
The README prominently advertises compatibility with Formtastic and SimpleForm, but spec/cocoon_spec.rb and the dummy app only show basic Rails form examples. Adding explicit integration tests using the dummy app's post/comment models with both form builders would verify the claim and catch breaking changes across form builders.
- [ ] Create spec/dummy/app/views with Formtastic and SimpleForm variants of the nested form (currently missing explicit examples)
- [ ] Add test cases in spec/cocoon_spec.rb that render forms with both builders and assert the correct DOM structure and data attributes
- [ ] Update gemfiles/Gemfile.formtastic and gemfiles/Gemfile.simple_form to exist (currently only .default and rails versions listed) for CI matrix testing
Add Rails 6+ and modern jQuery compatibility tests to .travis.yml
The README claims compatibility with Rails 3, 4, and 5, but .travis.yml and gemfiles/ only show configs for Rails 3.2.13 and 4. Modern Rails 6+ and 7 have different asset pipelines and jQuery handling; adding these test matrices would prevent silent breakage and document support level.
- [ ] Add gemfiles/Gemfile.rails-6 and gemfiles/Gemfile.rails-7 with appropriate Rails and jQuery dependencies
- [ ] Update .travis.yml to test against new gemfiles in the build matrix
- [ ] Verify or update lib/cocoon/view_helpers.rb for asset pipeline compatibility (Rails 6+ uses Webpacker/Importmap by default)
🌿Good first issues
- Add comprehensive documentation to README.markdown for handling dynamic field validation errors (currently only basic CRUD is documented; no examples of server-side error display on re-rendered nested fields).
- Write integration tests in spec/cocoon_spec.rb for Rails 6 Webpacker + npm package (@nathanvda/cocoon) to ensure ES6 import works alongside jQuery (currently only asset pipeline tested).
- Add example app demonstrating deeply nested forms (3+ levels: project → tasks → subtasks) in spec/dummy/app/views to show handling of cascading validations and destroy callbacks.
⭐Top contributors
Click to expand
Top contributors
- @nathanvda — 66 commits
- @Liooo — 4 commits
- @ViliusLuneckas — 3 commits
- @simi — 3 commits
- @entretechno-jeremiah — 2 commits
📝Recent commits
Click to expand
Recent commits
b3f4e6d— Merge pull request #622 from p8/update-history-to-1.2.15 (nathanvda)c5a82e2— Update History.md to latest changes (p8)47ba8ae— Merge pull request #618 from mishina2228/https (nathanvda)ed82517— Use https for GitHub link (mishina2228)208db1c— Merge pull request #615 from dogomedia/fix_jquery_deprecation (nathanvda)8abb318— Fix jquery deprecation. (jcdogo)1becbd9— Merge pull request #610 from ConfusedVorlon/patch-1 (nathanvda)665fb0b— flag the has one gotcha (ConfusedVorlon)59b6a18— Merge pull request #600 from entretechno/hotwired-turbo (nathanvda)72ccd8f— Merge pull request #605 from DrCool2/patch-1 (nathanvda)
🔒Security observations
Failed to generate security analysis.
LLM-derived; treat as a starting point, not a security audit.
👉Where to read next
- Open issues — current backlog
- Recent PRs — what's actively shipping
- Source on GitHub
Generated by RepoPilot. Verdict based on maintenance signals — see the live page for receipts. Re-run on a new commit to refresh.