Nested forms with older versions of Rails involved installing gems and enduring a minor pain in the ass to get everything working, but thanks to StimulusJS and the following approach of using HTML <template> tags (found on Drifting Ruby) it’s now much easier and cleaner to add nested forms to a has_and_belongs_to_many associated model.

# models/post.rb

class Post < ApplicationRecord
	has_many :categories, dependent: :destroy
	accepts_nested_attributes_for :categories, allow_destroy: true, reject_if: proc { |attr| attr['label'].blank? }
end
# models/category.rb

class Category < ApplicationRecord
	belongs_to :post
end
# posts_controller.rb

def post_params
	params.require(:post).permit(
		:name,
		categories_attributes: [
			:_destroy,
			:id,
			:name
		]
	)
end
// app/javascript/controllers/posts_form_controller.js

import { Controller } from "@hotwired/stimulus"

export default class extends Controller {
	static targets = ["add_item", "template"]

	connect() {
	}

	add_association() {
		event.preventDefault()
		var content = this.templateTarget.innerHTML.replace(/TEMPLATE_RECORD/g, new Date().valueOf())
		event.target.insertAdjacentHTML('beforebegin', content)
	}

	remove_association() {
		event.preventDefault()
		let item = event.target.closest("article")
		item.querySelector("input[name*='_destroy']").value = 1
		item.style.display = 'none'
	}
}
# posts/_form.html.erb

<div data-controller="posts-form">

	<template data-posts-form-target='template'>
		<article>
			<%= form.fields_for :categories, Category.new, child_index: 'TEMPLATE_RECORD' do | category | %>
				<%= render 'categories_fields', form: category %>
			<% end %>
		</article>
	</template>

	<article>
		<%= form.fields_for :categories do | category | %>
			<%= render 'categories_fields', form: category %>
		<% end %>
	</article>

	<%= link_to "+ Add Category",
		"#",
		role: "button",
		data: { action: "posts-form#add_association" }
	%>
</div>
# posts_categories_fields.html.erb

<%= form.label :name %>
<%= form.text_field :name %>

<p>
	<%= link_to "Remove",
		"#",
		role: "button",
		class: "outline",
		data: { action: "click->posts-form#remove_association" }
	%>
</p>