diff --git a/scipost_django/journals/forms.py b/scipost_django/journals/forms.py
index 04760df64342ad60caf6a1792d614a75cf77d81b..4c22f8114ee1b4ee75d7a71b1f3c120b71ed6ed7 100644
--- a/scipost_django/journals/forms.py
+++ b/scipost_django/journals/forms.py
@@ -44,6 +44,7 @@ from .models import (
     PublicationAuthorsTable,
 )
 from .utils import JournalUtils
+from .validators import doi_validator
 
 
 from common.utils import get_current_domain, jatsify_tags
@@ -133,8 +134,57 @@ class PublicationSearchForm(forms.Form):
         return publications
 
 
+class CitationListItemForm(forms.ModelForm):
+    doi = forms.CharField(
+        required=False,
+        widget=forms.TextInput(attrs={"placeholder": "DOI"}),
+        validators=[doi_validator],
+    )
+
+    class Meta:
+        model = Publication
+        fields = []
+
+    def __init__(self, *args, **kwargs):
+        self.index = kwargs.pop("index")
+        citation_list = kwargs["instance"].metadata["citation_list"]
+        if self.index < len(citation_list):
+            kwargs["initial"] = {"doi": citation_list[self.index]["doi"]}
+
+        super().__init__(*args, **kwargs)
+
+    def clean_doi(self):
+        doi = self.cleaned_data.get("doi")
+        dois_in_list = [cite["doi"] for cite in self.instance.metadata["citation_list"]]
+        if doi in dois_in_list and doi != self.initial.get("doi"):
+            self.add_error("doi", "This DOI is already in the citation list.")
+        return doi
+
+    def save(self, *args, **kwargs):
+        doi = self.cleaned_data.get("doi")
+        entry = {"key": "ref" + str(self.index + 1), "doi": doi}
+
+        if self.index < len(self.instance.metadata["citation_list"]):
+            self.instance.metadata["citation_list"][self.index] = entry
+        else:
+            self.instance.metadata["citation_list"].append(entry)
+
+        # Resort the citation list
+        sorted_list = sorted(
+            self.instance.metadata["citation_list"], key=lambda x: int(x["key"][3:])
+        )
+        self.instance.metadata["citation_list"] = [
+            {"key": "ref" + str(n + 1), "doi": cite["doi"]}
+            for n, cite in enumerate(sorted_list)
+        ]
+        return super().save(*args, **kwargs)
+
+
 class CitationListBibitemsForm(forms.ModelForm):
-    latex_bibitems = forms.CharField(widget=forms.Textarea())
+    latex_bibitems = forms.CharField(
+        widget=forms.Textarea(),
+        help_text="Once you submit, it will overwrite the current citation list, shown below.",
+    )
 
     class Meta:
         model = Publication
@@ -653,9 +703,9 @@ class DraftPublicationForm(forms.ModelForm):
                 s.id for s in self.submission.specialties.all()
             ]
             self.fields["approaches"].initial = self.submission.approaches
-            self.fields[
-                "submission_date"
-            ].initial = self.submission.original_submission_date
+            self.fields["submission_date"].initial = (
+                self.submission.original_submission_date
+            )
             self.fields["acceptance_date"].initial = self.submission.acceptance_date
             self.fields["publication_date"].initial = timezone.now()
 
diff --git a/scipost_django/journals/regexes.py b/scipost_django/journals/regexes.py
index a8275410dfb68773343a42c25f68d0a4ffebb228..db6ed340c3f2e62b38c51e1d333601249734662c 100644
--- a/scipost_django/journals/regexes.py
+++ b/scipost_django/journals/regexes.py
@@ -15,3 +15,5 @@ PUBLICATION_DOI_LABEL_REGEX += (
 
 DOI_DISPATCH_PATTERN = r"(?P<journal_tag>{})".format(JOURNAL_DOI_LABEL_REGEX)
 DOI_DISPATCH_PATTERN += r"(\.(?P<part_1>\w+)(\.(?P<part_2>[0-9]+)(\.(?P<part_3>[0-9]{3,}))?)?)?(-(?P<suffix>[0-9]+(\.[0-9]+)?))?"
+
+CROSSREF_DOI_REGEX = r"^10.\d{4,9}/[-._;()/:a-zA-Z0-9]+$"
diff --git a/scipost_django/journals/templates/journals/_hx_citation_list_bibitems_form.html b/scipost_django/journals/templates/journals/_hx_citation_list_bibitems_form.html
new file mode 100644
index 0000000000000000000000000000000000000000..c8ffda114df14c7b319659d3798d8f77bfead1db
--- /dev/null
+++ b/scipost_django/journals/templates/journals/_hx_citation_list_bibitems_form.html
@@ -0,0 +1,8 @@
+{% load bootstrap %}
+
+<form action="{% url 'journals:create_citation_list_metadata' publication.doi_label %}"
+      method="post">
+  {% csrf_token %}
+  {{ form|bootstrap }}
+  <input type="submit" class="btn btn-primary" value="Submit" />
+</form>
diff --git a/scipost_django/journals/templates/journals/_hx_citation_list_item.html b/scipost_django/journals/templates/journals/_hx_citation_list_item.html
new file mode 100644
index 0000000000000000000000000000000000000000..86ef11916ffbb5cb75071bc1dc44908316006573
--- /dev/null
+++ b/scipost_django/journals/templates/journals/_hx_citation_list_item.html
@@ -0,0 +1,20 @@
+<tr>
+  <td>{{ citation.key }}</td>
+  <td>{{ citation.doi }}</td>
+  <td>
+    <button class="btn btn-light btn-sm"
+            title="Edit"
+            hx-target="closest tr"
+            hx-swap="outerHTML"
+            hx-get="{% url "journals:_hx_citation_list_item_form" doi_label=publication.doi_label index=index %}">
+      <span>{% include "bi/pencil-square.html" %}</span>
+    </button>
+    <button class="btn btn-light btn-sm"
+            title="Delete"
+            hx-target="closest tr"
+            hx-swap="outerHTML"
+            hx-get="{% url "journals:_hx_citation_list_item_delete" doi_label=publication.doi_label index=index %}">
+      <span class="text-danger">{% include "bi/trash-fill.html" %}</span>
+    </button>
+  </td>
+</tr>
diff --git a/scipost_django/journals/templates/journals/_hx_citation_list_item_form.html b/scipost_django/journals/templates/journals/_hx_citation_list_item_form.html
new file mode 100644
index 0000000000000000000000000000000000000000..39b3366f64df1322fc4be129f1dfc1970459be7a
--- /dev/null
+++ b/scipost_django/journals/templates/journals/_hx_citation_list_item_form.html
@@ -0,0 +1,18 @@
+<tr hx-post="{{ request.get_full_path }}"
+    hx-swap="outerHTML"
+    hx-trigger="click from:closest tr target:button[type='submit']"
+    hx-include="this">
+
+    <td>
+      ref{{ index|add:1 }}
+    </td>
+  {% for field in form %}<td>{{ field }}
+    {% for error in field.errors %}
+    <div class="text-danger">{{ error }}</div>
+    {% endfor %}
+  </td>{% endfor %}
+
+  <td>
+    <button class="btn btn-sm btn-primary" type="submit">Save</button>
+  </td>
+</tr>
diff --git a/scipost_django/journals/templates/journals/create_citation_list_metadata.html b/scipost_django/journals/templates/journals/create_citation_list_metadata.html
index 985181fc5786c1bb226957b9200861e9e6668451..7b97a52966bc8422c9c6804fd88f6b23d7b47337 100644
--- a/scipost_django/journals/templates/journals/create_citation_list_metadata.html
+++ b/scipost_django/journals/templates/journals/create_citation_list_metadata.html
@@ -1,6 +1,8 @@
 {% extends 'scipost/base.html' %}
 
-{% block pagetitle %}: Create citation list metadata{% endblock pagetitle %}
+{% block pagetitle %}
+  : Create citation list metadata
+{% endblock pagetitle %}
 
 {% block breadcrumb %}
   <div class="breadcrumb-container">
@@ -14,37 +16,39 @@
   </div>
 {% endblock %}
 
-{% load bootstrap %}
-
 {% block content %}
 
-  <div class="row">
-    <div class="col-12">
-      <h1 class="highlight">Create citation list metadata page for <a href="{{ publication.get_absolute_url }}">{{ publication.doi_label }}</a></h1>
-      <p>
-        The following field is prefilled with the current citation list of the Publication object. Once you submit, it will overwrite the current citation list, shown below.
-      </p>
-      <br>
-
-      <form action="{% url 'journals:create_citation_list_metadata' publication.doi_label %}" method="post">
-        {% csrf_token %}
-        {{ form|bootstrap }}
-        <input type="submit" class="btn btn-primary" value="Submit">
-        <a href="{% url 'journals:manage_metadata' %}" class="ms-3 btn btn-link">Back to Admin</a>
-      </form>
-
-      <hr class="divider">
-
-      <h3>Current citation list metadata:</h3>
-      <br>
-      <table class="table">
-        {% for citation in publication.metadata.citation_list %}
-          <tr>
-            <td>{{ citation.key }}</td><td>{{ citation.doi }}</td>
-          </tr>
-        {% endfor %}
-      </table>
-    </div>
-  </div>
+  <h1 class="highlight d-flex justify-content-between align-items-center">
+    <span>Create citation list metadata page for <a href="{{ publication.get_absolute_url }}">{{ publication.doi_label }}</a></span>
+    <a class="fs-6 btn-link" href="{% url 'journals:manage_metadata' %}">Back to Admin</a>
+  </h1>
+
+  <div id="paste-bibtex-form"
+       hx-get="{% url "journals:_hx_citation_list_bibitems_form" doi_label=publication.doi_label %}"
+       hx-trigger="load once"></div>
+
+  <h3 class="mt-4">Current citation list metadata:</h3>
+ 
+  <table class="table table-sm align-middle">
+
+    {% for citation in publication.metadata.citation_list %}
+      {% with index=forloop.counter|add:-1 %}
+        {% include "journals/_hx_citation_list_item.html" %}
+      {% endwith %}
+    {% endfor %}
+
+    <tr>
+      <td colspan="20" class="p-1 bg-opacity-10 bg-info text-center">
+        <button class="btn btn-link"
+                hx-target="closest tr"
+                hx-swap="beforebegin"
+                hx-get="{% url "journals:_hx_citation_list_item_form" doi_label=publication.doi_label %}">
+          <span class="me-1">{% include "bi/plus-square-fill.html" %}</span>
+          Add Item
+        </button>
+      </td>
+    </tr>
+
+  </table>
 
 {% endblock %}
diff --git a/scipost_django/journals/urls/general.py b/scipost_django/journals/urls/general.py
index ffdb76f006533f062d57dcc0074311966391b874..d80b7979ff2f1dc01920c7fe8f2b32656e49e4dd 100644
--- a/scipost_django/journals/urls/general.py
+++ b/scipost_django/journals/urls/general.py
@@ -128,6 +128,26 @@ urlpatterns = [
         journals_views.CitationUpdateView.as_view(),
         name="create_citation_list_metadata",
     ),
+    path(
+        "admin/<publication_doi_label:doi_label>/_hx_citation_list_bibitems_form",
+        journals_views._hx_citation_list_bibitems_form,
+        name="_hx_citation_list_bibitems_form",
+    ),
+    path(
+        "admin/<publication_doi_label:doi_label>/_hx_citation_list_item_form",
+        journals_views._hx_citation_list_item_form,
+        name="_hx_citation_list_item_form",
+    ),
+    path(
+        "admin/<publication_doi_label:doi_label>/_hx_citation_list_item_form/<int:index>",
+        journals_views._hx_citation_list_item_form,
+        name="_hx_citation_list_item_form",
+    ),
+    path(
+        "admin/<publication_doi_label:doi_label>/_hx_citation_list_item_delete/<int:index>",
+        journals_views._hx_citation_list_item_delete,
+        name="_hx_citation_list_item_delete",
+    ),
     path(
         "admin/<publication_doi_label:doi_label>/abstract_jats",
         journals_views.AbstractJATSUpdateView.as_view(),
diff --git a/scipost_django/journals/validators.py b/scipost_django/journals/validators.py
index 5e0ecabed4095077ea33da8d12abcc486e88bb4a..45305d148ed03162e3504c30e3f296cb3d5cec97 100644
--- a/scipost_django/journals/validators.py
+++ b/scipost_django/journals/validators.py
@@ -9,6 +9,7 @@ from .regexes import (
     VOLUME_DOI_LABEL_REGEX,
     ISSUE_DOI_LABEL_REGEX,
     PUBLICATION_DOI_LABEL_REGEX,
+    CROSSREF_DOI_REGEX,
 )
 
 doi_journal_validator = RegexValidator(
@@ -27,3 +28,7 @@ doi_publication_validator = RegexValidator(
     r"^{regex}$".format(regex=PUBLICATION_DOI_LABEL_REGEX),
     "Only expressions with regex %s are allowed." % PUBLICATION_DOI_LABEL_REGEX,
 )
+doi_validator = RegexValidator(
+    r"^{regex}$".format(regex=CROSSREF_DOI_REGEX),
+    "Only expressions with regex %s are allowed." % CROSSREF_DOI_REGEX,
+)
diff --git a/scipost_django/journals/views.py b/scipost_django/journals/views.py
index f9f4580180390d98d950a87be1bb46c554ac5295..dae0d5ab58a31e314892f588d978a937b4b879b9 100644
--- a/scipost_django/journals/views.py
+++ b/scipost_django/journals/views.py
@@ -14,6 +14,8 @@ import requests
 
 import matplotlib
 
+from scipost.permissions import HTMXPermissionsDenied, HTMXResponse
+
 matplotlib.use("Agg")
 import matplotlib.pyplot as plt
 import io, base64
@@ -61,6 +63,7 @@ from .models import (
 )
 from .forms import (
     AbstractJATSForm,
+    CitationListItemForm,
     DraftPublicationUpdateForm,
     FundingInfoForm,
     HTMXInlinePublicationResourceForm,
@@ -938,6 +941,118 @@ class CitationUpdateView(
         )
 
 
+def _hx_citation_list_item_delete(request, doi_label, index: int):
+    """
+    Deletes a citation entry at the given index.
+    """
+    if not request.user.has_perm("scipost.can_draft_publication"):
+        return HTMXPermissionsDenied(
+            "You do not have permission to delete a citation in this Publication"
+        )
+
+    publication = get_object_or_404(Publication, doi_label=doi_label)
+    if (
+        not request.user.has_perm("scipost.can_publish_accepted_submission")
+        and not publication.is_draft
+    ):
+        return HTMXPermissionsDenied(
+            "You do not have permission to delete a citation in this non-draft Publication"
+        )
+
+    publication.metadata["citation_list"].pop(index)
+    publication.save()
+
+    return HttpResponse("")
+
+
+def _hx_citation_list_item_form(request, doi_label, index: int | None = None):
+    """
+    Renders a form to create or edit a citation entry at the given index.
+    """
+    if not request.user.has_perm("scipost.can_draft_publication"):
+        return HTMXPermissionsDenied(
+            "You do not have permission to edit this Publication"
+        )
+
+    publication = get_object_or_404(Publication, doi_label=doi_label)
+    if (
+        not request.user.has_perm("scipost.can_publish_accepted_submission")
+        and not publication.is_draft
+    ):
+        return HTMXPermissionsDenied(
+            "You do not have permission to edit this non-draft Publication"
+        )
+
+    if index is not None:
+        if index >= len(publication.metadata["citation_list"]):
+            return HTMXResponse("Index out of range", tag="danger")
+        else:
+            form = CitationListItemForm(
+                request.POST or None,
+                instance=publication,
+                index=index,
+            )
+    else:
+        index = len(publication.metadata["citation_list"])
+        form = CitationListItemForm(
+            request.POST or None, instance=publication, index=index,
+        )
+
+    if request.method == "POST":
+        if form.is_valid():
+            form.save()
+            doi = form.cleaned_data.get("doi")
+            return TemplateResponse(
+                request,
+                "journals/_hx_citation_list_item.html",
+                {
+                    "citation": {"key": "ref" + str(index + 1), "doi": doi},
+                    "publication": publication,
+                    "index": index,
+                },
+            )
+
+    return TemplateResponse(
+        request,
+        "journals/_hx_citation_list_item_form.html",
+        {
+            "form": form,
+            "publication": publication,
+            "index": index,
+        },
+    )
+
+
+def _hx_citation_list_bibitems_form(request, doi_label):
+    if not request.user.has_perm("scipost.can_draft_publication"):
+        return HTMXPermissionsDenied(
+            "You do not have permission to edit this Publication"
+        )
+
+    publication = get_object_or_404(Publication, doi_label=doi_label)
+    if (
+        not request.user.has_perm("scipost.can_publish_accepted_submission")
+        and not publication.is_draft
+    ):
+        return HTMXPermissionsDenied(
+            "You do not have permission to edit this non-draft Publication"
+        )
+
+    form = CitationListBibitemsForm(request.POST or None, instance=publication)
+    if form.is_valid():
+        form.save()
+        messages.success(request, "Citation list updated")
+        return HttpResponse("")
+
+    context = {
+        "form": form,
+        "publication": publication,
+    }
+    return TemplateResponse(
+        request, "journals/_hx_citation_list_bibitems_form.html", context
+    )
+
+
 class AbstractJATSUpdateView(
     PublicationMixin, ProdSupervisorPublicationPermissionMixin, UpdateView
 ):
@@ -1406,9 +1521,7 @@ def adjust_pubfracs(request, doi_label):
         pubfrac, created = PubFrac.objects.get_or_create(
             publication=publication, organization=org
         )
-    formset = PubFracsFormSet(
-        request.POST or None, queryset=publication.pubfracs.all()
-    )
+    formset = PubFracsFormSet(request.POST or None, queryset=publication.pubfracs.all())
     if formset.is_valid():
         formset.save()
         messages.success(request, "Funding fractions successfully allocated.")