A trick you can use here is that Django allows you to change the formset's min_num
after it's created, letting you return any form index in a formset. The thing that you want to substitute with htmx is the new form and the management form, so you can create a view returning those:
def new_form(request: HttpRequest, i: int):
formset = MyFormset()
formset.min_num = i + 1
context = {'next': i + 1,
'form': formset.forms[i],
'management_form': formset.management_form}
return render(request, 'myapp/partial/new_form.html', context)
where the template looks like
<div class="form">{{ form }}</div>
<div id="new_form">
{{ management_form.TOTAL_FORMS }}
<button hx-target="#new_form" hx-get="/new_form/{{ next }}">
Add Form
</button>
</div>
Notice how the management form is inside the thing htmx replaces.
This approach keeps the benefit of formsets that they are all submitted at once rather than individually.