diff --git a/src/postorius/templates/postorius/user/list_options.html b/src/postorius/templates/postorius/user/list_options.html
index ca08666..7ec9e69 100644
--- a/src/postorius/templates/postorius/user/list_options.html
+++ b/src/postorius/templates/postorius/user/list_options.html
@@ -14,11 +14,11 @@
 {% trans 'Use this form to change the email used for this subscription' %}:
 
 
 
 
 {% endblock main %}
diff --git a/src/postorius/templates/postorius/user/mailman_settings.html b/src/postorius/templates/postorius/user/mailman_settings.html
index b2ccb6d..d2fb9ad 100644
--- a/src/postorius/templates/postorius/user/mailman_settings.html
+++ b/src/postorius/templates/postorius/user/mailman_settings.html
@@ -24,7 +24,7 @@
         
{% trans 'You are not yet subscribed to any lists, so you have no Mailman preferences.' %}
     {% else %}
         
     {% endif %}
 
diff --git a/src/postorius/templatetags/bootstrap_tags.py b/src/postorius/templatetags/bootstrap_tags.py
deleted file mode 100644
index 1bfcb88..0000000
--- a/src/postorius/templatetags/bootstrap_tags.py
+++ /dev/null
@@ -1,44 +0,0 @@
-# -*- coding: utf-8 -*-
-# Copyright (C) 1998-2016 by the Free Software Foundation, Inc.
-#
-# This file is part of Postorius.
-#
-# Postorius is free software: you can redistribute it and/or modify it under
-# the terms of the GNU General Public License as published by the Free
-# Software Foundation, either version 3 of the License, or (at your option)
-# any later version.
-#
-# Postorius is distributed in the hope that it will be useful, but WITHOUT
-# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
-# FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
-# more details.
-#
-# You should have received a copy of the GNU General Public License along with
-# Postorius.  If not, see 
.
-
-from django import template
-
-
-register = template.Library()
-
-
-@register.filter(name='add_form_control')
-def add_form_control(field):
-    return field.as_widget(attrs={'class': 'form-control'})
-
-
-@register.filter('fieldtype')
-def fieldtype(field):
-    return field.field.widget.__class__.__name__
-
-
-@register.inclusion_tag('postorius/lib/form-horizontal.html',
-                        takes_context=True)
-def render_form_horizontal(context, form, size_left=2, size_right=8,
-                           button=None):
-    return dict(
-        form=form,
-        size_left=size_left,
-        size_right=size_right,
-        button=button,
-        )
diff --git a/src/postorius/tests/mailman_api_tests/test_list_member_options.py b/src/postorius/tests/mailman_api_tests/test_list_member_options.py
index 6fd7fa9..0470c6a 100644
--- a/src/postorius/tests/mailman_api_tests/test_list_member_options.py
+++ b/src/postorius/tests/mailman_api_tests/test_list_member_options.py
@@ -19,10 +19,10 @@
 from django.conf import settings
 from django.contrib.auth.models import User
 from django.core.urlresolvers import reverse
+from django_mailman3.lib.mailman import get_mailman_client
 from six.moves.urllib_parse import quote
 
 from postorius.tests.utils import ViewTestCase
-from postorius.utils import get_client
 from postorius.forms import MemberModeration, UserPreferences
 
 
@@ -34,7 +34,7 @@
 
     def setUp(self):
         super(ListMembersOptionsTest, self).setUp()
-        self.domain = get_client().create_domain('example.com')
+        self.domain = get_mailman_client().create_domain('example.com')
         self.foo_list = self.domain.create_list('foo')
         self.user = User.objects.create_user(
             'testuser', 'test@example.com', 'testpass')
@@ -46,7 +46,7 @@
             'testmoderator', 'moderator@example.com', 'testpass')
         self.foo_list.add_owner('owner@example.com')
         self.foo_list.add_moderator('moderator@example.com')
-        self.mm_user = get_client().create_user('test@example.com', '')
+        self.mm_user = get_mailman_client().create_user('test@example.com', '')
         self.mm_user.addresses[0].verify()
         self.foo_list.subscribe('test@example.com', pre_verified=True,
                                 pre_confirmed=True, pre_approved=True)
diff --git a/src/postorius/tests/utils.py b/src/postorius/tests/utils.py
index 47b0ec0..a0b8c29 100644
--- a/src/postorius/tests/utils.py
+++ b/src/postorius/tests/utils.py
@@ -24,9 +24,9 @@
 from django.test import TestCase
 from mock import MagicMock
 from six.moves.urllib_parse import quote
+from django_mailman3.lib.mailman import get_mailman_client
 from django_mailman3.tests.utils import get_flash_messages
 
-from postorius.utils import get_client
 from mailmanclient.testing.vcr_helpers import get_vcr
 
 vcr_log = logging.getLogger('vcr')
@@ -98,7 +98,7 @@
     _mm_vcr = get_vcr(cassette_library_dir=_fixtures_dir)
 
     def setUp(self):
-        self.mm_client = get_client()
+        self.mm_client = get_mailman_client()
         if self.use_vcr:
             cm = self._mm_vcr.use_cassette('.'.join([
                 self.__class__.__name__, self._testMethodName, 'yaml']))
diff --git a/src/postorius/utils.py b/src/postorius/utils.py
index b99a5ad..e78ef1b 100644
--- a/src/postorius/utils.py
+++ b/src/postorius/utils.py
@@ -17,23 +17,13 @@
 # Postorius.  If not, see 
.
 import logging
 
-from django.conf import settings
-from django.core.paginator import Paginator, EmptyPage, PageNotAnInteger
 from django.shortcuts import render
-from django.utils.functional import cached_property
-from mailmanclient import Client
 from django.utils.translation import gettext as _
 
 
 logger = logging.getLogger(__name__)
 
 
-def get_client():
-    return Client('{0}/3.0'.format(settings.MAILMAN_REST_API_URL),
-                  settings.MAILMAN_REST_API_USER,
-                  settings.MAILMAN_REST_API_PASS)
-
-
 def render_api_error(request):
     """Renders an error template.
     Use if MailmanApiError is catched.
@@ -43,52 +33,6 @@
                               'Please start Mailman core.')})
 
 
-class MailmanPaginator(Paginator):
-    """
-    Subclass of Django's Paginator that works with MailmanClient's 'get_*_page'
-    functions. Use the function reference as the first argument::
-
-        MailmanPaginator(get_member_page, 25)
-
-    """
-
-    def __init__(self, function, per_page, **kwargs):
-        self.function = function
-        super(MailmanPaginator, self).__init__(None, per_page, **kwargs)
-
-    def page(self, number):
-        """
-        Returns a Page object for the given 1-based page number.
-        """
-        number = self.validate_number(number)
-        result = self.function(count=self.per_page, page=number)
-        return self._get_page(result, number, self)
-
-    @cached_property
-    def count(self):
-        """
-        Returns the total number of objects, across all pages.
-        """
-        # For now we need to get the first page to have the total_size.
-        # Mitigate the price of this call by using count=1.
-        return self.function(count=1, page=1).total_size
-
-
-def paginate(request, collection, count=20, paginator_class=Paginator):
-    # count is the number of items per page
-    paginator = paginator_class(collection, count)
-    page = request.GET.get('page')
-    try:
-        results = paginator.page(page)
-    except PageNotAnInteger:
-        # If page is not an integer, deliver first page.
-        results = paginator.page(1)
-    except EmptyPage:
-        # If page is out of range (e.g. 9999), deliver last page of results.
-        results = paginator.page(paginator.num_pages)
-    return results
-
-
 def set_other_emails(user):
     from postorius.models import MailmanUser, MailmanApiError, Mailman404Error
     if hasattr(user, 'other_emails'):
diff --git a/src/postorius/views/domain.py b/src/postorius/views/domain.py
index ef6f9f3..919161c 100644
--- a/src/postorius/views/domain.py
+++ b/src/postorius/views/domain.py
@@ -21,6 +21,7 @@
 from django.contrib.auth.decorators import login_required, user_passes_test
 from django.shortcuts import render, redirect
 from django.utils.translation import gettext as _
+from django_mailman3.lib.mailman import get_mailman_client
 try:
     from urllib2 import HTTPError
 except ImportError:
@@ -74,7 +75,7 @@
     """
     if request.method == 'POST':
         try:
-            client = utils.get_client()
+            client = get_mailman_client()
             client.delete_domain(domain)
             messages.success(request,
                              _('The domain %s has been deleted.' % domain))
diff --git a/src/postorius/views/generic.py b/src/postorius/views/generic.py
index 8fe6cec..1ceda01 100644
--- a/src/postorius/views/generic.py
+++ b/src/postorius/views/generic.py
@@ -18,6 +18,7 @@
 
 
 from django.views.generic import TemplateView
+from django_mailman3.lib.mailman import get_mailman_client
 
 from postorius.models import (List, MailmanUser, MailmanApiError,
                               Mailman404Error)
@@ -31,7 +32,7 @@
 
     def client(self):
         if getattr(self, '_client', None) is None:
-            self._client = utils.get_client()
+            self._client = get_mailman_client()
         return self._client
 
 
diff --git a/src/postorius/views/list.py b/src/postorius/views/list.py
index 6944176..19309bf 100644
--- a/src/postorius/views/list.py
+++ b/src/postorius/views/list.py
@@ -31,6 +31,8 @@
 from django.core.exceptions import ValidationError
 from django.utils.decorators import method_decorator
 from django.utils.translation import gettext as _
+from django_mailman3.lib.mailman import get_mailman_client
+from django_mailman3.lib.paginator import paginate, MailmanPaginator
 try:
     from urllib2 import HTTPError
 except ImportError:
@@ -102,9 +104,11 @@
                 return mailing_list.find_members(query, count=count, page=page)
         else:
             find_method = mailing_list.get_member_page
-        context['members'] = utils.paginate(
-            request, find_method, count=request.GET.get('count', 25),
-            paginator_class=utils.MailmanPaginator)
+        context['members'] = paginate(
+            find_method,
+            request.GET.get('page'),
+            request.GET.get('count', 25),
+            paginator_class=MailmanPaginator)
         if mailing_list.member_count == 0:
             context['empty_error'] = _('List has no Subscribers')
         else:
@@ -130,7 +134,7 @@
 @list_owner_required
 def list_member_options(request, list_id, email):
     template_name = 'postorius/lists/memberoptions.html'
-    client = utils.get_client()
+    client = get_mailman_client()
     mm_list = List.objects.get_or_404(fqdn_listname=list_id)
     try:
         mm_member = client.get_member(list_id, email)
@@ -315,7 +319,7 @@
 @login_required
 @list_owner_required
 def list_mass_subscribe(request, list_id):
-    mailing_list = utils.get_client().get_list(list_id)
+    mailing_list = get_mailman_client().get_list(list_id)
     if request.method == 'POST':
         form = ListMassSubscription(request.POST)
         if form.is_valid():
@@ -391,7 +395,7 @@
 @login_required
 @list_moderator_required
 def list_moderation(request, list_id, held_id=-1):
-    mailing_list = utils.get_client().get_list(list_id)
+    mailing_list = get_mailman_client().get_list(list_id)
     if request.method == 'POST':
         form = MultipleChoiceForm(request.POST)
         if form.is_valid():
@@ -415,9 +419,9 @@
                 messages.error(request, _('Message could not be found'))
     else:
         form = MultipleChoiceForm()
-    held_messages = utils.paginate(
-        request, mailing_list.get_held_page,
-        count=request.GET.get('count', 20),
+    held_messages = paginate(
+        mailing_list.get_held_page,
+        request.GET.get('page'), request.GET.get('count'),
         paginator_class=utils.MailmanPaginator)
     context = {
         'list': mailing_list,
@@ -538,6 +542,8 @@
     if request.user.is_superuser:
         only_public = False
     try:
+        # FIXME: this is not paginated, all lists will
+        # always be retrieved.
         lists = sorted(List.objects.all(only_public=only_public),
                        key=lambda l: l.fqdn_listname)
         logger.debug(lists)
@@ -546,8 +552,9 @@
     choosable_domains = _get_choosable_domains(request)
     return render(request, template,
                   {'count_options': [10, 25, 50, 100, 200], 'error': error,
-                   'lists': utils.paginate(request, lists,
-                                           count=request.GET.get('count', 10)),
+                   'lists': paginate(
+                       lists, request.GET.get('page'),
+                       request.GET.get('count', 10)),
                    'domain_count': len(choosable_domains)})
 
 
diff --git a/tox.ini b/tox.ini
index aa72404..d37bd48 100644
--- a/tox.ini
+++ b/tox.ini
@@ -1,17 +1,23 @@
 [tox]
 envlist = py27-django{18,19,110},pep8
 
+
 [base]
 deps =
   git+https://gitlab.com/mailman/mailmanclient.git
+  git+https://gitlab.com/mailman/django_mailman3.git
   mock
   vcrpy
   coverage
 
+
 [testenv]
 usedevelop = True
 deps =
     {[base]deps}
+    dev: -e../django-mailman3
+    dev: -e../mailmanclient
+    dev: Django>=1.8,<1.9
     django18: Django>=1.8,<1.9
     django19: Django>=1.9,<1.10
     django110: Django>=1.10,<1.11
@@ -21,48 +27,19 @@
     coverage report
 setenv =
     PYTHONPATH = {toxinidir}
-
-[testenv:record]
-basepython = python2.7
-deps =
-    {[base]deps}
-    Django==1.8
-setenv =
-    PYTHONPATH = {toxinidir}
-    POSTORIUS_VCR_RECORD_MODE = all
-commands =
-    python example_project/manage.py test --settings=test_settings {posargs:postorius}
+    record: POSTORIUS_VCR_RECORD_MODE = all
 
 
-# These are used for local development and expect mailman.client to be
-# sitting in a directory next to this one.
 [testenv:dev]
-usedevelop = True
-basepython = python2.7
-deps =
-    {[base]deps}
-    Django==1.8
-setenv =
-    PYTHONPATH = {toxinidir}
 commands =
-    # Install mailmanclient from local repo instead of from pypi
-    pip install -e ../mailmanclient
     python example_project/manage.py test --settings=test_settings {posargs:postorius}
 
+
 [testenv:dev-record]
-usedevelop = True
-basepython = python2.7
-deps =
-    {[base]deps}
-    Django==1.8
-setenv =
-    PYTHONPATH = {toxinidir}
-    POSTORIUS_VCR_RECORD_MODE = all
 commands =
-    # Install mailmanclient from local repo instead of from pypi
-    pip install -e ../mailmanclient
     python example_project/manage.py test --settings=test_settings {posargs:postorius}
 
+
 [testenv:pep8]
 basepython = python2.7
 deps =
@@ -71,6 +48,7 @@
 commands =
     flake8 {posargs}
 
+
 [flake8]
 ignore = E123, E133
 show-source = True