You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@myfaces.apache.org by lo...@apache.org on 2024/11/04 14:14:42 UTC

(myfaces-tobago) branch tobago-5.x updated: Tobago 5.x sheet paginator (#5672)

This is an automated email from the ASF dual-hosted git repository.

lofwyr pushed a commit to branch tobago-5.x
in repository https://gitbox.apache.org/repos/asf/myfaces-tobago.git


The following commit(s) were added to refs/heads/tobago-5.x by this push:
     new e2a0ca5fe0 Tobago 5.x sheet paginator (#5672)
e2a0ca5fe0 is described below

commit e2a0ca5fe0dde124514bab299152cfa664e99b0e
Author: Udo Schnurpfeil <lo...@apache.org>
AuthorDate: Mon Nov 4 15:14:36 2024 +0100

    Tobago 5.x sheet paginator (#5672)
    
    * feat: Reimplement paginator for sheet
    
    issue: TOBAGO-2243
---
 README.md                                          |   4 +
 .../myfaces/tobago/component/Attributes.java       |   3 +
 .../apache/myfaces/tobago/component/Pageable.java  |  60 ++++++
 .../myfaces/tobago/component/RendererTypes.java    |   8 +
 .../org/apache/myfaces/tobago/component/Tags.java  |   8 +
 .../internal/component/AbstractUIPaginator.java    |  45 +++++
 .../component/AbstractUIPaginatorList.java         |  28 +++
 .../component/AbstractUIPaginatorPage.java         |  23 +++
 .../component/AbstractUIPaginatorPanel.java        |  23 +++
 .../internal/component/AbstractUIPaginatorRow.java |  23 +++
 .../tobago/internal/component/AbstractUISheet.java |  44 ++++-
 .../renderkit/renderer/PaginatorListRenderer.java  | 157 +++++++++++++++
 .../renderkit/renderer/PaginatorPageRenderer.java  | 137 +++++++++++++
 .../renderkit/renderer/PaginatorPanelRenderer.java |  55 ++++++
 .../renderkit/renderer/PaginatorRenderer.java      |  85 ++++++++
 .../renderkit/renderer/PaginatorRowRenderer.java   | 127 ++++++++++++
 .../internal/renderkit/renderer/SheetRenderer.java | 209 +++++++++++++-------
 .../component/PaginatorListTagDeclaration.java     |  58 ++++++
 .../component/PaginatorPageTagDeclaration.java     |  58 ++++++
 .../component/PaginatorPanelTagDeclaration.java    |  52 +++++
 .../component/PaginatorRowTagDeclaration.java      |  48 +++++
 .../taglib/component/SheetTagDeclaration.java      |  81 +++++++-
 .../internal/taglib/declaration/HasArrows.java     |  39 ++++
 .../taglib/declaration/IsAlwaysVisible.java        |  33 ++++
 .../org/apache/myfaces/tobago/layout/Arrows.java   |  61 ++++++
 .../myfaces/tobago/layout/PaginatorMode.java       |  86 ++++++++
 .../tobago/renderkit/css/BootstrapClass.java       |   1 +
 .../tobago/renderkit/html/HtmlElements.java        |   4 +
 .../internal/config/AbstractTobagoTestBase.java    |  25 ++-
 .../renderkit/renderer/SheetRendererUnitTest.java  |  38 ++++
 .../myfaces/tobago/layout/ArrowsUnitTest.java      |  31 +++
 .../tobago/layout/PaginatorModeUnitTest.java       |  31 +++
 .../renderer/sheet/sheet-paginatorList.html        |  71 +++++++
 .../resources/renderer/sheet/sheet-paging.html     |  54 ++---
 .../myfaces/tobago/example/demo/AstroData.java     |   7 +-
 .../tobago/example/demo/SheetController.java       |  17 ++
 .../myfaces/tobago/example/demo/SolarObject.java   |  18 +-
 .../content/080-sheet/15-paginator/Paginator.xhtml |  78 ++++++++
 .../3000-sheet/40-paginator/Paginator.xhtml        |  89 +++++++++
 .../Paginator_Backward_Compatibility.xhtml         | 217 +++++++++++++++++++++
 tobago-theme/src/main/scss/_tobago.scss            |  73 +++++++
 .../src/main/css/tobago.css                        |  36 ++++
 .../src/main/css/tobago.css.map                    |   2 +-
 .../src/main/css/tobago.min.css                    |   2 +-
 .../src/main/css/tobago.min.css.map                |   2 +-
 .../src/main/css/tobago.css                        |  36 ++++
 .../src/main/css/tobago.css.map                    |   2 +-
 .../src/main/css/tobago.min.css                    |   2 +-
 .../src/main/css/tobago.min.css.map                |   2 +-
 .../src/main/css/tobago.css                        |  36 ++++
 .../src/main/css/tobago.css.map                    |   2 +-
 .../src/main/css/tobago.min.css                    |   2 +-
 .../src/main/css/tobago.min.css.map                |   2 +-
 .../tobago-theme-speyside/src/main/css/tobago.css  |  36 ++++
 .../src/main/css/tobago.css.map                    |   2 +-
 .../src/main/css/tobago.min.css                    |   2 +-
 .../src/main/css/tobago.min.css.map                |   2 +-
 .../tobago-theme-standard/src/main/css/tobago.css  |  36 ++++
 .../src/main/css/tobago.css.map                    |   2 +-
 .../src/main/css/tobago.min.css                    |   2 +-
 .../src/main/css/tobago.min.css.map                |   2 +-
 .../tobago-theme-standard/src/main/js/tobago.js    |   8 +-
 .../src/main/js/tobago.js.map                      |   2 +-
 .../src/main/ts/tobago-init.ts                     |   2 +
 .../src/main/ts/tobago-page.ts                     |  10 +-
 .../src/main/ts/tobago-paginator-page.ts           |  31 +++
 .../src/main/ts/tobago-paginator-row.ts            |  31 +++
 .../src/main/ts/tobago-paginator.ts                | 102 ++++++++++
 .../src/main/ts/tobago-sheet.ts                    |  11 ++
 .../org/apache/myfaces/tobago/apt/component.stg    |   8 +
 70 files changed, 2588 insertions(+), 136 deletions(-)

diff --git a/README.md b/README.md
index 603bcab7d1..4c08a566fc 100644
--- a/README.md
+++ b/README.md
@@ -28,6 +28,10 @@ cd tobago-example/tobago-example-demo
 mvn clean jetty:run -Pjetty
 ```
 
+* Mode 1: old | compatible | classic | useShowAttributes
+* Mode 2: auto
+* Mode 3: custom
+
 Browse to the local URL http://localhost:8080/
 
 ## Demo - TomEE
diff --git a/tobago-core/src/main/java/org/apache/myfaces/tobago/component/Attributes.java b/tobago-core/src/main/java/org/apache/myfaces/tobago/component/Attributes.java
index f329628a1a..03b89f3a8c 100644
--- a/tobago-core/src/main/java/org/apache/myfaces/tobago/component/Attributes.java
+++ b/tobago-core/src/main/java/org/apache/myfaces/tobago/component/Attributes.java
@@ -37,6 +37,8 @@ public enum Attributes {
   align,
   alignItems,
   alt,
+  alwaysVisible,
+  arrows,
   autocomplete,
   autoReload,
   autoSpacing,
@@ -324,6 +326,7 @@ public enum Attributes {
    * Used by a layout manager
    */
   paddingTop,
+  paginator,
   pagingTarget,
   password,
   placeholder,
diff --git a/tobago-core/src/main/java/org/apache/myfaces/tobago/component/Pageable.java b/tobago-core/src/main/java/org/apache/myfaces/tobago/component/Pageable.java
new file mode 100644
index 0000000000..200a62c80b
--- /dev/null
+++ b/tobago-core/src/main/java/org/apache/myfaces/tobago/component/Pageable.java
@@ -0,0 +1,60 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.myfaces.tobago.component;
+
+import org.apache.myfaces.tobago.internal.component.AbstractUILink;
+import org.apache.myfaces.tobago.internal.component.AbstractUISheet;
+import org.apache.myfaces.tobago.util.ComponentUtils;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import javax.faces.component.UIComponent;
+import javax.faces.component.behavior.AjaxBehavior;
+import javax.faces.context.FacesContext;
+import java.lang.invoke.MethodHandles;
+import java.util.Map;
+
+public interface Pageable {
+
+  Logger LOG = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass());
+
+  String SUFFIX_PAGE_ACTION = "pageAction"; // XXX may be renamed
+
+default AbstractUILink ensurePagingCommand(
+      final FacesContext facesContext, final AbstractUISheet sheet, final String facet, final String id,
+      final boolean disabled) {
+
+    final Map<String, UIComponent> facets = sheet.getFacets();
+    AbstractUILink command = (AbstractUILink) facets.get(facet);
+    if (command == null) {
+      command = (AbstractUILink) ComponentUtils.createComponent(facesContext, Tags.link.componentType(),
+          RendererTypes.Link, SUFFIX_PAGE_ACTION + id);
+      command.setRendered(true);
+      command.setDisabled(disabled);
+      command.setTransient(true);
+      facets.put(facet, command);
+
+      // add AjaxBehavior
+      final AjaxBehavior behavior = sheet.createReloadBehavior(sheet);
+      command.addClientBehavior("click", behavior);
+    }
+    return command;
+  }
+}
diff --git a/tobago-core/src/main/java/org/apache/myfaces/tobago/component/RendererTypes.java b/tobago-core/src/main/java/org/apache/myfaces/tobago/component/RendererTypes.java
index bad01f551a..b7643cf2e9 100644
--- a/tobago-core/src/main/java/org/apache/myfaces/tobago/component/RendererTypes.java
+++ b/tobago-core/src/main/java/org/apache/myfaces/tobago/component/RendererTypes.java
@@ -57,6 +57,10 @@ public enum RendererTypes {
   Operation,
   Out,
   Page,
+  PaginatorList,
+  PaginatorPage,
+  PaginatorPanel,
+  PaginatorRow,
   Panel,
   Popover,
   Popup,
@@ -131,6 +135,10 @@ public enum RendererTypes {
   public static final String OPERATION = "Operation";
   public static final String OUT = "Out";
   public static final String PAGE = "Page";
+  public static final String PAGINATOR_LIST = "PaginatorList";
+  public static final String PAGINATOR_PAGE = "PaginatorPage";
+  public static final String PAGINATOR_PANEL = "PaginatorPanel";
+  public static final String PAGINATOR_ROW = "PaginatorRow";
   public static final String PANEL = "Panel";
   public static final String POPOVER = "Popover";
   public static final String POPUP = "Popup";
diff --git a/tobago-core/src/main/java/org/apache/myfaces/tobago/component/Tags.java b/tobago-core/src/main/java/org/apache/myfaces/tobago/component/Tags.java
index a49cee0068..273fab6bc2 100644
--- a/tobago-core/src/main/java/org/apache/myfaces/tobago/component/Tags.java
+++ b/tobago-core/src/main/java/org/apache/myfaces/tobago/component/Tags.java
@@ -62,6 +62,10 @@ public enum Tags {
   operation,
   out,
   page,
+  paginatorList,
+  paginatorPage,
+  paginatorPanel,
+  paginatorRow,
   panel,
   popover,
   popup,
@@ -138,6 +142,10 @@ public enum Tags {
   public static final String OPERATION = "operation";
   public static final String OUT = "out";
   public static final String PAGE = "page";
+  public static final String PAGINATOR_LIST = "paginatorList";
+  public static final String PAGINATOR_PAGE = "paginatorPage";
+  public static final String PAGINATOR_PANEL = "paginatorPanel";
+  public static final String PAGINATOR_ROW = "paginatorRow";
   public static final String PANEL = "panel";
   public static final String POPOVER = "popover";
   public static final String POPUP = "popup";
diff --git a/tobago-core/src/main/java/org/apache/myfaces/tobago/internal/component/AbstractUIPaginator.java b/tobago-core/src/main/java/org/apache/myfaces/tobago/internal/component/AbstractUIPaginator.java
new file mode 100644
index 0000000000..a12d545e6c
--- /dev/null
+++ b/tobago-core/src/main/java/org/apache/myfaces/tobago/internal/component/AbstractUIPaginator.java
@@ -0,0 +1,45 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.myfaces.tobago.internal.component;
+
+import org.apache.myfaces.tobago.component.Visual;
+import org.apache.myfaces.tobago.util.ComponentUtils;
+
+import javax.faces.component.UIComponent;
+import javax.faces.component.UIOutput;
+
+public abstract class AbstractUIPaginator extends UIOutput implements Visual {
+
+  public abstract String getFor();
+
+  public abstract boolean isAlwaysVisible();
+
+    // todo: might be an interface instead of AbstractUISheet
+  public AbstractUISheet getPageable() {
+    final String forId = getFor();
+    if (forId != null) {
+      final UIComponent component = findComponent(forId);
+      if (component != null) {
+        return (AbstractUISheet) component;
+      }
+    }
+    return ComponentUtils.findAncestor(this, AbstractUISheet.class);
+  }
+}
diff --git a/tobago-core/src/main/java/org/apache/myfaces/tobago/internal/component/AbstractUIPaginatorList.java b/tobago-core/src/main/java/org/apache/myfaces/tobago/internal/component/AbstractUIPaginatorList.java
new file mode 100644
index 0000000000..619a885f19
--- /dev/null
+++ b/tobago-core/src/main/java/org/apache/myfaces/tobago/internal/component/AbstractUIPaginatorList.java
@@ -0,0 +1,28 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.myfaces.tobago.internal.component;
+
+import org.apache.myfaces.tobago.layout.Arrows;
+
+public abstract class AbstractUIPaginatorList extends AbstractUIPaginator {
+
+  public abstract Arrows getArrows();
+
+}
diff --git a/tobago-core/src/main/java/org/apache/myfaces/tobago/internal/component/AbstractUIPaginatorPage.java b/tobago-core/src/main/java/org/apache/myfaces/tobago/internal/component/AbstractUIPaginatorPage.java
new file mode 100644
index 0000000000..3d22f1515f
--- /dev/null
+++ b/tobago-core/src/main/java/org/apache/myfaces/tobago/internal/component/AbstractUIPaginatorPage.java
@@ -0,0 +1,23 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.myfaces.tobago.internal.component;
+
+public abstract class AbstractUIPaginatorPage extends AbstractUIPaginator {
+}
diff --git a/tobago-core/src/main/java/org/apache/myfaces/tobago/internal/component/AbstractUIPaginatorPanel.java b/tobago-core/src/main/java/org/apache/myfaces/tobago/internal/component/AbstractUIPaginatorPanel.java
new file mode 100644
index 0000000000..39ab1911ee
--- /dev/null
+++ b/tobago-core/src/main/java/org/apache/myfaces/tobago/internal/component/AbstractUIPaginatorPanel.java
@@ -0,0 +1,23 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.myfaces.tobago.internal.component;
+
+public abstract class AbstractUIPaginatorPanel extends AbstractUIPanelBase {
+}
diff --git a/tobago-core/src/main/java/org/apache/myfaces/tobago/internal/component/AbstractUIPaginatorRow.java b/tobago-core/src/main/java/org/apache/myfaces/tobago/internal/component/AbstractUIPaginatorRow.java
new file mode 100644
index 0000000000..26cc7c0f69
--- /dev/null
+++ b/tobago-core/src/main/java/org/apache/myfaces/tobago/internal/component/AbstractUIPaginatorRow.java
@@ -0,0 +1,23 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.myfaces.tobago.internal.component;
+
+public abstract class AbstractUIPaginatorRow extends AbstractUIPaginator {
+}
diff --git a/tobago-core/src/main/java/org/apache/myfaces/tobago/internal/component/AbstractUISheet.java b/tobago-core/src/main/java/org/apache/myfaces/tobago/internal/component/AbstractUISheet.java
index f028dc756c..0dfd650b19 100644
--- a/tobago-core/src/main/java/org/apache/myfaces/tobago/internal/component/AbstractUISheet.java
+++ b/tobago-core/src/main/java/org/apache/myfaces/tobago/internal/component/AbstractUISheet.java
@@ -20,6 +20,7 @@
 package org.apache.myfaces.tobago.internal.component;
 
 import org.apache.myfaces.tobago.component.Attributes;
+import org.apache.myfaces.tobago.component.Pageable;
 import org.apache.myfaces.tobago.component.Visual;
 import org.apache.myfaces.tobago.event.PageActionEvent;
 import org.apache.myfaces.tobago.event.SheetStateChangeEvent;
@@ -32,6 +33,7 @@ import org.apache.myfaces.tobago.internal.layout.OriginCell;
 import org.apache.myfaces.tobago.internal.util.SortingUtils;
 import org.apache.myfaces.tobago.layout.Measure;
 import org.apache.myfaces.tobago.layout.MeasureList;
+import org.apache.myfaces.tobago.layout.PaginatorMode;
 import org.apache.myfaces.tobago.layout.ShowPosition;
 import org.apache.myfaces.tobago.model.ExpandedState;
 import org.apache.myfaces.tobago.model.ScrollPosition;
@@ -46,6 +48,8 @@ import javax.el.MethodExpression;
 import javax.el.ValueExpression;
 import javax.faces.component.UIColumn;
 import javax.faces.component.UIComponent;
+import javax.faces.component.behavior.AjaxBehavior;
+import javax.faces.component.behavior.ClientBehavior;
 import javax.faces.component.behavior.ClientBehaviorHolder;
 import javax.faces.context.FacesContext;
 import javax.faces.event.AbortProcessingException;
@@ -66,7 +70,7 @@ import java.util.List;
  */
 @ListenerFor(systemEventClass = PreRenderComponentEvent.class)
 public abstract class AbstractUISheet extends AbstractUIData
-    implements SheetStateChangeSource, SortActionSource, ClientBehaviorHolder, Visual,
+    implements SheetStateChangeSource, SortActionSource, ClientBehaviorHolder, Visual, Pageable,
     ComponentSystemEventListener {
 
   private static final Logger LOG = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass());
@@ -568,6 +572,34 @@ public abstract class AbstractUISheet extends AbstractUIData
     this.lazyFirstRow = lazyFirstRow;
   }
 
+  public AjaxBehavior createReloadBehavior(final AbstractUISheet sheet) {
+    final AjaxBehavior reloadBehavior = findReloadBehavior(sheet);
+    final ArrayList<String> renderIds = new ArrayList<>();
+    renderIds.add(sheet.getId());
+    if (reloadBehavior != null) {
+      renderIds.addAll(reloadBehavior.getRender());
+    }
+    final ArrayList<String> executeIds = new ArrayList<>();
+    executeIds.add(sheet.getId());
+    if (reloadBehavior != null) {
+      executeIds.addAll(reloadBehavior.getExecute());
+    }
+    final AjaxBehavior behavior = new AjaxBehavior();
+    behavior.setExecute(executeIds);
+    behavior.setRender(renderIds);
+    behavior.setTransient(true);
+    return behavior;
+  }
+
+  private AjaxBehavior findReloadBehavior(final ClientBehaviorHolder holder) {
+    final List<ClientBehavior> reload = holder.getClientBehaviors().get("reload");
+    if (reload != null && !reload.isEmpty() && reload.get(0) instanceof AjaxBehavior) {
+      return (AjaxBehavior) reload.get(0);
+    } else {
+      return null;
+    }
+  }
+
   @Override
   public boolean isRendersRowContainer() {
     return true;
@@ -593,19 +625,29 @@ public abstract class AbstractUISheet extends AbstractUIData
     this.headerGrid = headerGrid;
   }
 
+  @Deprecated(since = "5.14.0, 6.6.0", forRemoval = true)
   public abstract boolean isShowDirectLinksArrows();
 
+  @Deprecated(since = "5.14.0, 6.6.0", forRemoval = true)
   public abstract boolean isShowPageRangeArrows();
 
+  @Deprecated(since = "5.14.0, 6.6.0", forRemoval = true)
   public abstract ShowPosition getShowRowRange();
 
+  @Deprecated(since = "5.14.0, 6.6.0", forRemoval = true)
   public abstract ShowPosition getShowPageRange();
 
+  @Deprecated(since = "5.14.0, 6.6.0", forRemoval = true)
   public abstract ShowPosition getShowDirectLinks();
 
+  @Deprecated(since = "5.14.0, 6.6.0", forRemoval = true)
+  public abstract Integer getDirectLinkCount();
+
   public abstract boolean isLazy();
 
   public abstract Integer getMaxSortColumns();
 
   public abstract Integer getLazyRows();
+
+  public abstract PaginatorMode getPaginator();
 }
diff --git a/tobago-core/src/main/java/org/apache/myfaces/tobago/internal/renderkit/renderer/PaginatorListRenderer.java b/tobago-core/src/main/java/org/apache/myfaces/tobago/internal/renderkit/renderer/PaginatorListRenderer.java
new file mode 100644
index 0000000000..567e3dd09b
--- /dev/null
+++ b/tobago-core/src/main/java/org/apache/myfaces/tobago/internal/renderkit/renderer/PaginatorListRenderer.java
@@ -0,0 +1,157 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.myfaces.tobago.internal.renderkit.renderer;
+
+import org.apache.myfaces.tobago.component.Attributes;
+import org.apache.myfaces.tobago.event.SheetAction;
+import org.apache.myfaces.tobago.internal.component.AbstractUIPaginatorList;
+import org.apache.myfaces.tobago.internal.component.AbstractUISheet;
+import org.apache.myfaces.tobago.layout.Arrows;
+import org.apache.myfaces.tobago.renderkit.css.BootstrapClass;
+import org.apache.myfaces.tobago.renderkit.css.Icons;
+import org.apache.myfaces.tobago.renderkit.html.HtmlElements;
+import org.apache.myfaces.tobago.util.ComponentUtils;
+import org.apache.myfaces.tobago.webapp.TobagoResponseWriter;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import javax.faces.context.FacesContext;
+import java.io.IOException;
+import java.lang.invoke.MethodHandles;
+import java.util.ArrayList;
+
+public class PaginatorListRenderer<T extends AbstractUIPaginatorList> extends PaginatorRenderer<T> {
+
+  private static final Logger LOG = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass());
+
+  @Override
+  public void decodeInternal(final FacesContext facesContext, final T paginator) {
+  }
+
+  @Override
+  public void encodeBeginInternal(final FacesContext facesContext, final T paginator) throws IOException {
+
+    final TobagoResponseWriter writer = getResponseWriter(facesContext);
+    final String clientId = paginator.getClientId(facesContext);
+    final AbstractUISheet sheet = paginator.getPageable();
+    final boolean visible = paginator.isAlwaysVisible() || sheet.needMoreThanOnePage();
+
+    writer.startElement(HtmlElements.TOBAGO_PAGINATOR_LIST);
+    writer.writeIdAttribute(clientId);
+    writer.writeClassAttribute(
+        visible ? null : BootstrapClass.D_NONE,
+        paginator.getCustomClass());
+    if (sheet != null) {
+      writer.startElement(HtmlElements.UL);
+      writer.writeClassAttribute(BootstrapClass.PAGINATION);
+
+      int linkCount = ComponentUtils.getIntAttribute(sheet, Attributes.directLinkCount);
+      linkCount--;  // current page needs no link
+      final ArrayList<Integer> prevs = new ArrayList<>(linkCount);
+      int page = sheet.getCurrentPage() + 1;
+      for (int i = 0; i < linkCount && page > 1; i++) {
+        page--;
+        if (page > 0) {
+          prevs.add(0, page);
+        }
+      }
+
+      final ArrayList<Integer> nexts = new ArrayList<>(linkCount);
+      page = sheet.getCurrentPage() + 1;
+      final int pages = sheet.hasRowCount() || sheet.isRowsUnlimited() ? sheet.getPages() : Integer.MAX_VALUE;
+      for (int i = 0; i < linkCount && page < pages; i++) {
+        page++;
+        if (page > 1) {
+          nexts.add(page);
+        }
+      }
+
+      if (prevs.size() > (linkCount / 2)
+          && nexts.size() > (linkCount - (linkCount / 2))) {
+        while (prevs.size() > (linkCount / 2)) {
+          prevs.remove(0);
+        }
+        while (nexts.size() > (linkCount - (linkCount / 2))) {
+          nexts.remove(nexts.size() - 1);
+        }
+      } else if (prevs.size() <= (linkCount / 2)) {
+        while (prevs.size() + nexts.size() > linkCount) {
+          nexts.remove(nexts.size() - 1);
+        }
+      } else {
+        while (prevs.size() + nexts.size() > linkCount) {
+          prevs.remove(0);
+        }
+      }
+
+      final Arrows arrows = paginator.getArrows();
+      if (arrows == Arrows.show || arrows == Arrows.auto) {
+        final boolean disabled = sheet.isAtBeginning();
+        encodeLink(
+            facesContext, sheet, disabled, SheetAction.first, null, Icons.SKIP_START, null);
+        encodeLink(facesContext, sheet, disabled, SheetAction.prev, null, Icons.CARET_LEFT, null);
+      }
+
+      int skip = prevs.size() > 0 ? prevs.get(0) : 1;
+      if (!(arrows == Arrows.show || arrows == Arrows.auto) && skip > 1) {
+        skip -= linkCount - (linkCount / 2);
+        skip--;
+        if (skip < 1) {
+          skip = 1;
+        }
+        encodeLink(facesContext, sheet, false, SheetAction.toPage, skip, Icons.THREE_DOTS, null);
+      }
+      for (final Integer prev : prevs) {
+        encodeLink(facesContext, sheet, false, SheetAction.toPage, prev, null, null);
+      }
+      encodeLink(facesContext, sheet, false, SheetAction.toPage,
+          sheet.getCurrentPage() + 1, null, BootstrapClass.ACTIVE);
+
+      for (final Integer next : nexts) {
+        encodeLink(facesContext, sheet, false, SheetAction.toPage, next, null, null);
+      }
+
+      skip = nexts.size() > 0 ? nexts.get(nexts.size() - 1) : pages;
+      if (!(arrows == Arrows.show || arrows == Arrows.auto) && skip < pages) {
+        skip += linkCount / 2;
+        skip++;
+        if (skip > pages) {
+          skip = pages;
+        }
+        encodeLink(facesContext, sheet, false, SheetAction.toPage, skip, Icons.THREE_DOTS, null);
+      }
+      if (arrows == Arrows.show || arrows == Arrows.auto) {
+        final boolean disabled = sheet.isAtEnd();
+        encodeLink(facesContext, sheet, disabled, SheetAction.next, null, Icons.CARET_RIGHT, null);
+        encodeLink(facesContext, sheet, disabled || !sheet.hasRowCount(), SheetAction.last, null,
+            Icons.SKIP_END, null);
+      }
+      writer.endElement(HtmlElements.UL);
+    } else {
+      LOG.warn("No sheet found for paginator with clientId='{}'!", clientId);
+    }
+  }
+
+  @Override
+  public void encodeEndInternal(final FacesContext facesContext, final T component) throws IOException {
+    final TobagoResponseWriter writer = getResponseWriter(facesContext);
+    writer.endElement(HtmlElements.TOBAGO_PAGINATOR_LIST);
+  }
+}
diff --git a/tobago-core/src/main/java/org/apache/myfaces/tobago/internal/renderkit/renderer/PaginatorPageRenderer.java b/tobago-core/src/main/java/org/apache/myfaces/tobago/internal/renderkit/renderer/PaginatorPageRenderer.java
new file mode 100644
index 0000000000..9b0d1b2766
--- /dev/null
+++ b/tobago-core/src/main/java/org/apache/myfaces/tobago/internal/renderkit/renderer/PaginatorPageRenderer.java
@@ -0,0 +1,137 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.myfaces.tobago.internal.renderkit.renderer;
+
+import org.apache.myfaces.tobago.component.Facets;
+import org.apache.myfaces.tobago.event.SheetAction;
+import org.apache.myfaces.tobago.internal.component.AbstractUILink;
+import org.apache.myfaces.tobago.internal.component.AbstractUIPaginatorPage;
+import org.apache.myfaces.tobago.internal.component.AbstractUISheet;
+import org.apache.myfaces.tobago.renderkit.css.BootstrapClass;
+import org.apache.myfaces.tobago.renderkit.css.Icons;
+import org.apache.myfaces.tobago.renderkit.css.TobagoClass;
+import org.apache.myfaces.tobago.renderkit.html.HtmlAttributes;
+import org.apache.myfaces.tobago.renderkit.html.HtmlElements;
+import org.apache.myfaces.tobago.renderkit.html.HtmlInputTypes;
+import org.apache.myfaces.tobago.util.ComponentUtils;
+import org.apache.myfaces.tobago.util.ResourceUtils;
+import org.apache.myfaces.tobago.webapp.TobagoResponseWriter;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import javax.faces.context.FacesContext;
+import java.io.IOException;
+import java.lang.invoke.MethodHandles;
+import java.text.MessageFormat;
+import java.util.Locale;
+
+public class PaginatorPageRenderer<T extends AbstractUIPaginatorPage> extends PaginatorRenderer<T> {
+
+  private static final Logger LOG = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass());
+
+  @Override
+  public void decodeInternal(final FacesContext facesContext, final T component) {
+  }
+
+  @Override
+  public void encodeBeginInternal(final FacesContext facesContext, final T paginator) throws IOException {
+
+    final TobagoResponseWriter writer = getResponseWriter(facesContext);
+    final AbstractUISheet sheet = paginator.getPageable();
+    final boolean visible = paginator.isAlwaysVisible() || sheet.needMoreThanOnePage();
+
+    writer.startElement(HtmlElements.TOBAGO_PAGINATOR_PAGE);
+    writer.writeIdAttribute(paginator.getClientId(facesContext));
+    writer.writeClassAttribute(
+        visible ? null : BootstrapClass.D_NONE,
+        BootstrapClass.PAGINATION,
+        paginator.getCustomClass());
+
+    final AbstractUILink command
+        = sheet.ensurePagingCommand(facesContext, sheet, Facets.pagerPage.name(), SheetAction.toPage.name(), false);
+    final String pagerCommandId = command.getClientId(facesContext);
+
+    if (sheet.isShowPageRangeArrows()) {
+      final boolean disabled = sheet.isAtBeginning();
+      encodeLink(
+          facesContext, sheet, disabled, SheetAction.first, null, Icons.SKIP_START, null);
+      encodeLink(facesContext, sheet, disabled, SheetAction.prev, null, Icons.CARET_LEFT, null);
+    }
+    writer.startElement(HtmlElements.LI);
+    writer.writeClassAttribute(BootstrapClass.PAGE_ITEM);
+    writer.startElement(HtmlElements.SPAN);
+    writer.writeClassAttribute(
+        TobagoClass.PAGING,
+        BootstrapClass.PAGE_LINK,
+        BootstrapClass.TEXT_NOWRAP);
+    writer.writeAttribute(HtmlAttributes.TITLE,
+        ResourceUtils.getString(facesContext, "sheet.setPage"), true);
+    if (sheet.getRowCount() != 0) {
+      final Locale locale = facesContext.getViewRoot().getLocale();
+      final int first = sheet.getCurrentPage() + 1;
+      final boolean unknown = !sheet.hasRowCount();
+      final int pages = unknown ? -1 : sheet.getPages();
+      final String key;
+      if (unknown) {
+        key = first == pages ? "sheet.pageX" : "sheet.pageXtoY";
+      } else {
+        key = first == pages ? "sheet.pageXofZ" : "sheet.pageXtoYofZ";
+      }
+      final String inputMarker = "{#}";
+      final Object[] args = {inputMarker, pages == -1 ? "?" : pages};
+      final MessageFormat detail = new MessageFormat(ResourceUtils.getString(facesContext, key), locale);
+      final String formatted = detail.format(args);
+      final int pos = formatted.indexOf(inputMarker);
+      if (pos >= 0) {
+        writer.writeText(formatted.substring(0, pos));
+        writer.startElement(HtmlElements.SPAN);
+        writer.writeText(Integer.toString(first));
+        writer.endElement(HtmlElements.SPAN);
+        writer.startElement(HtmlElements.INPUT);
+        writer.writeIdAttribute(pagerCommandId);
+        writer.writeNameAttribute(pagerCommandId);
+        writer.writeAttribute(HtmlAttributes.TYPE, HtmlInputTypes.TEXT);
+        writer.writeAttribute(HtmlAttributes.VALUE, first);
+        if (!unknown) {
+          writer.writeAttribute(HtmlAttributes.MAXLENGTH, Integer.toString(pages).length());
+        }
+        writer.endElement(HtmlElements.INPUT);
+        writer.writeText(formatted.substring(pos + inputMarker.length()));
+      } else {
+        writer.writeText(formatted);
+      }
+    }
+    ComponentUtils.removeFacet(sheet, Facets.pagerPage);
+    writer.endElement(HtmlElements.SPAN);
+    writer.endElement(HtmlElements.LI);
+    if (sheet.isShowPageRangeArrows()) {
+      final boolean disabled = sheet.isAtEnd();
+      encodeLink(facesContext, sheet, disabled, SheetAction.next, null, Icons.CARET_RIGHT, null);
+      encodeLink(facesContext, sheet, disabled || !sheet.hasRowCount(), SheetAction.last, null,
+          Icons.SKIP_END, null);
+    }
+  }
+
+  @Override
+  public void encodeEndInternal(final FacesContext facesContext, final T component) throws IOException {
+    final TobagoResponseWriter writer = getResponseWriter(facesContext);
+    writer.endElement(HtmlElements.TOBAGO_PAGINATOR_PAGE);
+  }
+}
diff --git a/tobago-core/src/main/java/org/apache/myfaces/tobago/internal/renderkit/renderer/PaginatorPanelRenderer.java b/tobago-core/src/main/java/org/apache/myfaces/tobago/internal/renderkit/renderer/PaginatorPanelRenderer.java
new file mode 100644
index 0000000000..2a3ee80ed6
--- /dev/null
+++ b/tobago-core/src/main/java/org/apache/myfaces/tobago/internal/renderkit/renderer/PaginatorPanelRenderer.java
@@ -0,0 +1,55 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.myfaces.tobago.internal.renderkit.renderer;
+
+import org.apache.myfaces.tobago.internal.component.AbstractUIPaginatorPanel;
+import org.apache.myfaces.tobago.renderkit.RendererBase;
+import org.apache.myfaces.tobago.renderkit.css.BootstrapClass;
+import org.apache.myfaces.tobago.renderkit.html.HtmlElements;
+import org.apache.myfaces.tobago.webapp.TobagoResponseWriter;
+
+import javax.faces.context.FacesContext;
+import java.io.IOException;
+
+public class PaginatorPanelRenderer<T extends AbstractUIPaginatorPanel> extends RendererBase<T> {
+
+  @Override
+  public void decodeInternal(final FacesContext facesContext, final T component) {
+  }
+
+  @Override
+  public void encodeBeginInternal(final FacesContext facesContext, final T paginator) throws IOException {
+
+    final TobagoResponseWriter writer = getResponseWriter(facesContext);
+    writer.startElement(HtmlElements.TOBAGO_PAGINATOR_PANEL);
+    writer.writeIdAttribute(paginator.getClientId(facesContext));
+    final int childCount = paginator.getChildCount();
+    writer.writeClassAttribute(
+        BootstrapClass.D_FLEX,
+        childCount > 1 ? BootstrapClass.JUSTIFY_CONTENT_BETWEEN : BootstrapClass.JUSTIFY_CONTENT_CENTER,
+        paginator.getCustomClass());
+  }
+
+  @Override
+  public void encodeEndInternal(final FacesContext facesContext, final T component) throws IOException {
+    final TobagoResponseWriter writer = getResponseWriter(facesContext);
+    writer.endElement(HtmlElements.TOBAGO_PAGINATOR_PANEL);
+  }
+}
diff --git a/tobago-core/src/main/java/org/apache/myfaces/tobago/internal/renderkit/renderer/PaginatorRenderer.java b/tobago-core/src/main/java/org/apache/myfaces/tobago/internal/renderkit/renderer/PaginatorRenderer.java
new file mode 100644
index 0000000000..90193ee911
--- /dev/null
+++ b/tobago-core/src/main/java/org/apache/myfaces/tobago/internal/renderkit/renderer/PaginatorRenderer.java
@@ -0,0 +1,85 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.myfaces.tobago.internal.renderkit.renderer;
+
+import org.apache.myfaces.tobago.component.Attributes;
+import org.apache.myfaces.tobago.event.SheetAction;
+import org.apache.myfaces.tobago.internal.component.AbstractUILink;
+import org.apache.myfaces.tobago.internal.component.AbstractUIPaginator;
+import org.apache.myfaces.tobago.internal.component.AbstractUISheet;
+import org.apache.myfaces.tobago.renderkit.RendererBase;
+import org.apache.myfaces.tobago.renderkit.css.BootstrapClass;
+import org.apache.myfaces.tobago.renderkit.css.CssItem;
+import org.apache.myfaces.tobago.renderkit.css.Icons;
+import org.apache.myfaces.tobago.renderkit.html.HtmlAttributes;
+import org.apache.myfaces.tobago.renderkit.html.HtmlButtonTypes;
+import org.apache.myfaces.tobago.renderkit.html.HtmlElements;
+import org.apache.myfaces.tobago.util.ComponentUtils;
+import org.apache.myfaces.tobago.util.ResourceUtils;
+import org.apache.myfaces.tobago.webapp.TobagoResponseWriter;
+
+import javax.faces.context.FacesContext;
+import java.io.IOException;
+import java.text.MessageFormat;
+import java.util.Locale;
+
+public abstract class PaginatorRenderer<T extends AbstractUIPaginator> extends RendererBase<T> {
+
+  protected void encodeLink(
+      final FacesContext facesContext, final AbstractUISheet data,
+      final boolean disabled, final SheetAction action, final Integer target, final Icons icon, final CssItem liClass)
+      throws IOException {
+
+    final String facet = action == SheetAction.toPage || action == SheetAction.toRow
+        ? action.name() + "-" + target
+        : action.name();
+    final AbstractUILink command = data.ensurePagingCommand(facesContext, data, facet, facet, disabled);
+    if (target != null) {
+      ComponentUtils.setAttribute(command, Attributes.pagingTarget, target);
+    }
+
+    final Locale locale = facesContext.getViewRoot().getLocale();
+    final String message = ResourceUtils.getString(facesContext, action.getBundleKey());
+    final String tip = new MessageFormat(message, locale).format(new Integer[]{target}); // needed fot ToPage
+
+    final TobagoResponseWriter writer = getResponseWriter(facesContext);
+    writer.startElement(HtmlElements.LI);
+    writer.writeClassAttribute(liClass, disabled ? BootstrapClass.DISABLED : null, BootstrapClass.PAGE_ITEM);
+    writer.startElement(HtmlElements.BUTTON);
+    writer.writeAttribute(HtmlAttributes.TYPE, HtmlButtonTypes.BUTTON);
+    writer.writeClassAttribute(BootstrapClass.PAGE_LINK);
+    writer.writeIdAttribute(command.getClientId(facesContext));
+    writer.writeAttribute(HtmlAttributes.TITLE, tip, true);
+    writer.writeAttribute(HtmlAttributes.DISABLED, disabled);
+    if (icon != null) {
+      writer.startElement(HtmlElements.I);
+      writer.writeClassAttribute(icon);
+      writer.endElement(HtmlElements.I);
+    } else {
+      writer.writeText(String.valueOf(target));
+    }
+    if (!disabled) {
+      encodeBehavior(writer, facesContext, command);
+    }
+    data.getFacets().remove(facet);
+    writer.endElement(HtmlElements.BUTTON);
+    writer.endElement(HtmlElements.LI);
+  }
+
+}
diff --git a/tobago-core/src/main/java/org/apache/myfaces/tobago/internal/renderkit/renderer/PaginatorRowRenderer.java b/tobago-core/src/main/java/org/apache/myfaces/tobago/internal/renderkit/renderer/PaginatorRowRenderer.java
new file mode 100644
index 0000000000..fc39e8a4cf
--- /dev/null
+++ b/tobago-core/src/main/java/org/apache/myfaces/tobago/internal/renderkit/renderer/PaginatorRowRenderer.java
@@ -0,0 +1,127 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.myfaces.tobago.internal.renderkit.renderer;
+
+import org.apache.myfaces.tobago.component.Facets;
+import org.apache.myfaces.tobago.event.SheetAction;
+import org.apache.myfaces.tobago.internal.component.AbstractUILink;
+import org.apache.myfaces.tobago.internal.component.AbstractUIPaginatorRow;
+import org.apache.myfaces.tobago.internal.component.AbstractUISheet;
+import org.apache.myfaces.tobago.internal.util.StringUtils;
+import org.apache.myfaces.tobago.renderkit.css.BootstrapClass;
+import org.apache.myfaces.tobago.renderkit.html.HtmlAttributes;
+import org.apache.myfaces.tobago.renderkit.html.HtmlElements;
+import org.apache.myfaces.tobago.renderkit.html.HtmlInputTypes;
+import org.apache.myfaces.tobago.util.ComponentUtils;
+import org.apache.myfaces.tobago.util.ResourceUtils;
+import org.apache.myfaces.tobago.webapp.TobagoResponseWriter;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import javax.faces.context.FacesContext;
+import java.io.IOException;
+import java.lang.invoke.MethodHandles;
+import java.text.MessageFormat;
+import java.util.Locale;
+
+public class PaginatorRowRenderer<T extends AbstractUIPaginatorRow> extends PaginatorRenderer<T> {
+
+  private static final Logger LOG = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass());
+
+  @Override
+  public void decodeInternal(final FacesContext facesContext, final T component) {
+  }
+
+  @Override
+  public void encodeBeginInternal(final FacesContext facesContext, final T paginator) throws IOException {
+
+    final TobagoResponseWriter writer = getResponseWriter(facesContext);
+    final AbstractUISheet sheet = paginator.getPageable();
+    final boolean visible = paginator.isAlwaysVisible() || sheet.needMoreThanOnePage();
+
+    writer.startElement(HtmlElements.TOBAGO_PAGINATOR_ROW);
+    writer.writeIdAttribute(paginator.getClientId(facesContext));
+    writer.writeClassAttribute(
+        visible ? null : BootstrapClass.D_NONE,
+        BootstrapClass.PAGINATION,
+        paginator.getCustomClass());
+    writer.startElement(HtmlElements.SPAN);
+    writer.writeClassAttribute(BootstrapClass.PAGE_ITEM);
+    writer.writeAttribute(HtmlAttributes.TITLE,
+        ResourceUtils.getString(facesContext, "sheet.setRow"), true);
+    writer.startElement(HtmlElements.SPAN);
+    writer.writeClassAttribute(
+        BootstrapClass.PAGE_LINK,
+        BootstrapClass.TEXT_NOWRAP);
+
+    final AbstractUILink command
+        = sheet.ensurePagingCommand(facesContext, sheet, Facets.pagerRow.name(), SheetAction.toRow.name(), false);
+    final String pagerCommandId = command.getClientId(facesContext);
+
+    if (sheet.getRowCount() != 0) {
+      final Locale locale = facesContext.getViewRoot().getLocale();
+      final int first = sheet.getFirst() + 1;
+      final int last1 = sheet.hasRowCount()
+          ? sheet.getLastRowIndexOfCurrentPage()
+          : -1;
+      final boolean unknown = !sheet.hasRowCount();
+      final String key; // plural
+      if (unknown) {
+        key = first == last1 ? "sheet.rowX" : "sheet.rowXtoY";
+      } else {
+        key = first == last1 ? "sheet.rowXofZ" : "sheet.rowXtoYofZ";
+      }
+      final String inputMarker = "{#}";
+      final Object[] args = {inputMarker, last1 == -1 ? "?" : last1, unknown ? "" : sheet.getRowCount()};
+      final MessageFormat detail = new MessageFormat(ResourceUtils.getString(facesContext, key), locale);
+      final String formatted = detail.format(args);
+      final int pos = formatted.indexOf(inputMarker);
+      if (pos >= 0) {
+        writer.writeText(formatted.substring(0, pos));
+        writer.startElement(HtmlElements.SPAN);
+        writer.writeText(Integer.toString(first));
+        writer.endElement(HtmlElements.SPAN);
+        writer.startElement(HtmlElements.INPUT);
+        writer.writeIdAttribute(pagerCommandId);
+        writer.writeNameAttribute(pagerCommandId);
+        writer.writeAttribute(HtmlAttributes.TYPE, HtmlInputTypes.TEXT);
+        writer.writeAttribute(HtmlAttributes.VALUE, first);
+        final int maxLength = Integer.toString(sheet.getRowCount()).length();
+        if (!unknown) {
+          writer.writeAttribute(HtmlAttributes.MAXLENGTH, maxLength);
+        }
+        writer.writeAttribute(HtmlAttributes.PATTERN, StringUtils.positiveNumberRegexp(maxLength), true);
+        writer.endElement(HtmlElements.INPUT);
+        writer.writeText(formatted.substring(pos + inputMarker.length()));
+      } else {
+        writer.writeText(formatted);
+      }
+    }
+    ComponentUtils.removeFacet(sheet, Facets.pagerRow);
+    writer.endElement(HtmlElements.SPAN);
+    writer.endElement(HtmlElements.SPAN);
+  }
+
+  @Override
+  public void encodeEndInternal(final FacesContext facesContext, final T component) throws IOException {
+    final TobagoResponseWriter writer = getResponseWriter(facesContext);
+    writer.endElement(HtmlElements.TOBAGO_PAGINATOR_ROW);
+  }
+}
diff --git a/tobago-core/src/main/java/org/apache/myfaces/tobago/internal/renderkit/renderer/SheetRenderer.java b/tobago-core/src/main/java/org/apache/myfaces/tobago/internal/renderkit/renderer/SheetRenderer.java
index 1c91702a8a..ae2df0581d 100644
--- a/tobago-core/src/main/java/org/apache/myfaces/tobago/internal/renderkit/renderer/SheetRenderer.java
+++ b/tobago-core/src/main/java/org/apache/myfaces/tobago/internal/renderkit/renderer/SheetRenderer.java
@@ -22,8 +22,13 @@ package org.apache.myfaces.tobago.internal.renderkit.renderer;
 import org.apache.myfaces.tobago.component.Attributes;
 import org.apache.myfaces.tobago.component.Facets;
 import org.apache.myfaces.tobago.component.LabelLayout;
+import org.apache.myfaces.tobago.component.Pageable;
 import org.apache.myfaces.tobago.component.RendererTypes;
 import org.apache.myfaces.tobago.component.Tags;
+import org.apache.myfaces.tobago.component.UIOut;
+import org.apache.myfaces.tobago.component.UIPaginatorList;
+import org.apache.myfaces.tobago.component.UIPaginatorPage;
+import org.apache.myfaces.tobago.component.UIPaginatorRow;
 import org.apache.myfaces.tobago.context.Markup;
 import org.apache.myfaces.tobago.event.PageActionEvent;
 import org.apache.myfaces.tobago.event.SheetAction;
@@ -36,6 +41,7 @@ import org.apache.myfaces.tobago.internal.component.AbstractUIColumnSelector;
 import org.apache.myfaces.tobago.internal.component.AbstractUIData;
 import org.apache.myfaces.tobago.internal.component.AbstractUILink;
 import org.apache.myfaces.tobago.internal.component.AbstractUIOut;
+import org.apache.myfaces.tobago.internal.component.AbstractUIPaginator;
 import org.apache.myfaces.tobago.internal.component.AbstractUIReload;
 import org.apache.myfaces.tobago.internal.component.AbstractUIRow;
 import org.apache.myfaces.tobago.internal.component.AbstractUISheet;
@@ -47,7 +53,8 @@ import org.apache.myfaces.tobago.internal.renderkit.CommandMap;
 import org.apache.myfaces.tobago.internal.util.HtmlRendererUtils;
 import org.apache.myfaces.tobago.internal.util.JsonUtils;
 import org.apache.myfaces.tobago.internal.util.RenderUtils;
-import org.apache.myfaces.tobago.internal.util.StringUtils;
+import org.apache.myfaces.tobago.layout.Arrows;
+import org.apache.myfaces.tobago.layout.PaginatorMode;
 import org.apache.myfaces.tobago.layout.ShowPosition;
 import org.apache.myfaces.tobago.layout.TextAlign;
 import org.apache.myfaces.tobago.model.ExpandedState;
@@ -59,13 +66,11 @@ import org.apache.myfaces.tobago.model.TreePath;
 import org.apache.myfaces.tobago.renderkit.RendererBase;
 import org.apache.myfaces.tobago.renderkit.css.BootstrapClass;
 import org.apache.myfaces.tobago.renderkit.css.CssItem;
-import org.apache.myfaces.tobago.renderkit.css.Icons;
 import org.apache.myfaces.tobago.renderkit.css.TobagoClass;
 import org.apache.myfaces.tobago.renderkit.html.Arias;
 import org.apache.myfaces.tobago.renderkit.html.CustomAttributes;
 import org.apache.myfaces.tobago.renderkit.html.DataAttributes;
 import org.apache.myfaces.tobago.renderkit.html.HtmlAttributes;
-import org.apache.myfaces.tobago.renderkit.html.HtmlButtonTypes;
 import org.apache.myfaces.tobago.renderkit.html.HtmlElements;
 import org.apache.myfaces.tobago.renderkit.html.HtmlInputTypes;
 import org.apache.myfaces.tobago.util.ComponentUtils;
@@ -82,16 +87,13 @@ import javax.faces.component.UIComponent;
 import javax.faces.component.UIData;
 import javax.faces.component.UINamingContainer;
 import javax.faces.component.behavior.AjaxBehavior;
-import javax.faces.component.behavior.ClientBehavior;
-import javax.faces.component.behavior.ClientBehaviorHolder;
 import javax.faces.context.FacesContext;
 import java.io.IOException;
 import java.lang.invoke.MethodHandles;
-import java.text.MessageFormat;
 import java.util.ArrayList;
 import java.util.Collections;
+import java.util.HashMap;
 import java.util.List;
-import java.util.Locale;
 import java.util.Map;
 import java.util.Objects;
 
@@ -105,7 +107,6 @@ public class SheetRenderer<T extends AbstractUISheet> extends RendererBase<T> {
   private static final String SUFFIX_SELECTED = ComponentUtils.SUB_SEPARATOR + "selected";
   private static final String SUFFIX_LAZY = NamingContainer.SEPARATOR_CHAR + "pageActionlazy";
   private static final String SUFFIX_LAZY_SCROLL_POSITION = ComponentUtils.SUB_SEPARATOR + "lazyScrollPosition";
-  private static final String SUFFIX_PAGE_ACTION = "pageAction";
   private static final String SUFFIX_COLUMN_SELECTOR = ComponentUtils.SUB_SEPARATOR + "columnSelector";
 
   @Override
@@ -193,7 +194,7 @@ public class SheetRenderer<T extends AbstractUISheet> extends RendererBase<T> {
     }
 
     final String sheetClientIdWithAction
-        = clientId + UINamingContainer.getSeparatorChar(facesContext) + SUFFIX_PAGE_ACTION;
+        = clientId + UINamingContainer.getSeparatorChar(facesContext) + Pageable.SUFFIX_PAGE_ACTION;
     if (sourceId != null && sourceId.startsWith(sheetClientIdWithAction)) {
       String actionString = sourceId.substring(sheetClientIdWithAction.length());
       int index = actionString.indexOf('-');
@@ -245,7 +246,85 @@ public class SheetRenderer<T extends AbstractUISheet> extends RendererBase<T> {
     final String sheetId = component.getClientId(facesContext);
     final Markup markup = component.getMarkup();
     final TobagoResponseWriter writer = getResponseWriter(facesContext);
-    final AbstractUIReload reload = ComponentUtils.getReloadFacet(component);
+
+    final PaginatorMode paginator = component.getPaginator();
+    LOG.info("paginator={}", paginator);
+
+    switch (paginator) {
+      case list, auto -> {
+        final UIPaginatorList paginatorList = (UIPaginatorList) ComponentUtils.createComponent(
+            facesContext, Tags.paginatorList.componentType(), RendererTypes.PaginatorList, "_paginator_list");
+        paginatorList.setTransient(true);
+
+        final UIComponent after = ensureAfterFacetPaginator(facesContext, component);
+        after.getChildren().add(paginatorList);
+      }
+      case page -> {
+        final UIPaginatorPage paginatorPage = (UIPaginatorPage) ComponentUtils.createComponent(
+            facesContext, Tags.paginatorPage.componentType(), RendererTypes.PaginatorPage, "_paginator_page");
+        paginatorPage.setTransient(true);
+
+        final UIComponent after = ensureAfterFacetPaginator(facesContext, component);
+        after.getChildren().add(paginatorPage);
+      }
+      case row -> {
+        final UIPaginatorRow paginatorRow = (UIPaginatorRow) ComponentUtils.createComponent(
+            facesContext, Tags.paginatorRow.componentType(), RendererTypes.PaginatorRow, "_paginator_row");
+        paginatorRow.setTransient(true);
+
+        final UIComponent after = ensureAfterFacetPaginator(facesContext, component);
+        after.getChildren().add(paginatorRow);
+      }
+      case useShowAttributes -> {
+        final Map<ShowPosition, AbstractUIPaginator> paginatorMap = new HashMap<>();
+
+        if (!ShowPosition.none.equals(component.getShowRowRange()) && component.isPagingVisible()) {
+          final UIPaginatorRow paginatorRow = (UIPaginatorRow) ComponentUtils.createComponent(
+              facesContext, Tags.paginatorRow.componentType(), RendererTypes.PaginatorRow, "_paginator_row");
+          paginatorRow.setAlwaysVisible(component.isShowPagingAlways());
+          paginatorRow.setTransient(true);
+          paginatorMap.put(component.getShowRowRange(), paginatorRow);
+        }
+
+        if (!ShowPosition.none.equals(component.getShowDirectLinks()) && component.isPagingVisible()) {
+          final UIPaginatorList paginatorList = (UIPaginatorList) ComponentUtils.createComponent(
+              facesContext, Tags.paginatorList.componentType(), RendererTypes.PaginatorList, "_paginator_list");
+          paginatorList.setAlwaysVisible(component.isShowPagingAlways());
+          paginatorList.setArrows(component.isShowDirectLinksArrows()? Arrows.show : Arrows.hide);
+          paginatorList.setMax(component.getDirectLinkCount());
+          paginatorList.setTransient(true);
+          paginatorMap.put(component.getShowDirectLinks(), paginatorList);
+        }
+
+        if (!ShowPosition.none.equals(component.getShowPageRange()) && component.isPagingVisible()) {
+          final UIPaginatorPage paginatorPage = (UIPaginatorPage) ComponentUtils.createComponent(
+              facesContext, Tags.paginatorPage.componentType(), RendererTypes.PaginatorPage, "_paginator_page");
+          paginatorPage.setAlwaysVisible(component.isShowPagingAlways());
+          paginatorPage.setTransient(true);
+          paginatorMap.put(component.getShowPageRange(), paginatorPage);
+        }
+
+        if (!paginatorMap.isEmpty()) {
+          final UIComponent after = ensureAfterFacetPaginator(facesContext, component);
+
+          final ShowPosition[] order = {ShowPosition.left, ShowPosition.center, ShowPosition.right};
+          for (ShowPosition showPosition : order) {
+            if (paginatorMap.containsKey(showPosition)) {
+              after.getChildren().add(paginatorMap.get(showPosition));
+            } else {
+              final UIOut space = (UIOut) ComponentUtils.createComponent(
+                  facesContext, Tags.out.componentType(), RendererTypes.Out, "_space_" + showPosition.name());
+              space.setTransient(true);
+              after.getChildren().add(space);
+            }
+          }
+        }
+      }
+      case custom -> {
+        // nothing to do
+      }
+      default -> throw new IllegalStateException("Unknown paginator mode: " + paginator);
+    }
 
     UIComponent header = component.getHeader();
     if (header == null) {
@@ -303,11 +382,23 @@ public class SheetRenderer<T extends AbstractUISheet> extends RendererBase<T> {
 
     encodeBehavior(writer, facesContext, component);
 
+    final AbstractUIReload reload = ComponentUtils.getReloadFacet(component);
     if (reload != null) {
       reload.encodeAll(facesContext);
     }
   }
 
+  private static <T extends AbstractUISheet> UIComponent ensureAfterFacetPaginator(
+      final FacesContext facesContext, final T component) {
+    UIComponent after = component.getFacet(Facets.AFTER);
+    if (after == null) {
+      after = ComponentUtils.createComponent(
+          facesContext, Tags.paginatorPanel.componentType(), RendererTypes.PaginatorPanel, "_after");
+      component.getFacets().put(Facets.AFTER, after);
+    }
+    return after;
+  }
+
   @Override
   public void encodeChildrenInternal(final FacesContext facesContext, final T component) throws IOException {
     for (final UIComponent child : component.getChildren()) {
@@ -370,14 +461,19 @@ public class SheetRenderer<T extends AbstractUISheet> extends RendererBase<T> {
         facesContext, component, writer, sheetId, selectable, columnWidths, selectedRows, columns, autoLayout,
         expandedValue);
 
+//    final boolean force = false;//!component.isAutoPaginator(); XXX is turned off
+//    LOG.info("force={}", force);
+
     if (component.isPagingVisible()) {
       writer.startElement(HtmlElements.FOOTER);
-
+/*
+      // XXX this block is old and replaced by Paginator*Renderer
+      // XXX TOBAGO-2243
       // show row range
       final ShowPosition showPositionRowRange = component.getShowRowRange();
-      if (showPositionRowRange != ShowPosition.none) {
-        final AbstractUILink command
-            = ensurePagingCommand(facesContext, component, Facets.pagerRow.name(), SheetAction.toRow.name(), false);
+      if (force && showPositionRowRange != ShowPosition.none) {
+        final AbstractUILink command = component.ensurePagingCommand(
+        facesContext, component, Facets.pagerRow.name(), SheetAction.toRow.name(), false);
         final String pagerCommandId = command.getClientId(facesContext);
 
         writer.startElement(HtmlElements.UL);
@@ -437,9 +533,11 @@ public class SheetRenderer<T extends AbstractUISheet> extends RendererBase<T> {
         writer.endElement(HtmlElements.UL);
       }
 
+      // XXX this block is old and replaced by Paginator*Renderer
+      // XXX TOBAGO-2243
       // show direct links
       final ShowPosition showPositionDirectLinks = component.getShowDirectLinks();
-      if (showPositionDirectLinks != ShowPosition.none) {
+      if (force && showPositionDirectLinks != ShowPosition.none) {
         writer.startElement(HtmlElements.UL);
         writer.writeClassAttribute(
             cssForLeftCenterRight(showPositionDirectLinks),
@@ -462,9 +560,9 @@ public class SheetRenderer<T extends AbstractUISheet> extends RendererBase<T> {
 
       // show page range
       final ShowPosition showPositionPageRange = component.getShowPageRange();
-      if (showPositionPageRange != ShowPosition.none) {
-        final AbstractUILink command
-            = ensurePagingCommand(facesContext, component, Facets.pagerPage.name(), SheetAction.toPage.name(), false);
+      if (force && showPositionPageRange != ShowPosition.none) {
+        final AbstractUILink command = component.ensurePagingCommand(
+        facesContext, component, Facets.pagerPage.name(), SheetAction.toPage.name(), false);
         final String pagerCommandId = command.getClientId(facesContext);
 
         writer.startElement(HtmlElements.UL);
@@ -531,10 +629,15 @@ public class SheetRenderer<T extends AbstractUISheet> extends RendererBase<T> {
         }
         writer.endElement(HtmlElements.UL);
       }
-
+*/
       writer.endElement(HtmlElements.FOOTER);
     }
 
+    final UIComponent after = component.getFacet(Facets.AFTER);
+    if (after != null) {
+      after.encodeAll(facesContext);
+    }
+
     if (component.isTreeModel()) {
       writer.startElement(HtmlElements.INPUT);
       writer.writeAttribute(HtmlAttributes.TYPE, HtmlInputTypes.HIDDEN);
@@ -565,6 +668,11 @@ public class SheetRenderer<T extends AbstractUISheet> extends RendererBase<T> {
     final Markup sheetMarkup = sheet.getMarkup() != null ? sheet.getMarkup() : Markup.NULL;
     final ExpandedState expandedState = sheet.isTreeModel() ? sheet.getExpandedState() : null;
 
+    final UIComponent before = sheet.getFacet(Facets.BEFORE);
+    if (before != null) {
+      before.encodeAll(facesContext);
+    }
+
     if (showHeader && !autoLayout) {
       // if no autoLayout, we render the header in a separate table.
 
@@ -915,7 +1023,7 @@ public class SheetRenderer<T extends AbstractUISheet> extends RendererBase<T> {
                   sortCommand = (AbstractUILink) ComponentUtils.createComponent(
                       facesContext, Tags.link.componentType(), RendererTypes.Link, sorterId);
                   sortCommand.setTransient(true);
-                  final AjaxBehavior reloadBehavior = createReloadBehavior(sheet);
+                  final AjaxBehavior reloadBehavior = sheet.createReloadBehavior(sheet);
                   sortCommand.addClientBehavior("click", reloadBehavior);
                   ComponentUtils.setFacet(column, Facets.sorter, sortCommand);
                 }
@@ -1108,6 +1216,9 @@ public class SheetRenderer<T extends AbstractUISheet> extends RendererBase<T> {
     return selected;
   }
 
+  // XXX this block is old and replaced by Paginator*Renderer
+  // XXX TOBAGO-2243
+  /*
   private void encodeLink(
       final FacesContext facesContext, final AbstractUISheet data, final Application application,
       final boolean disabled, final SheetAction action, final Integer target, final Icons icon, final CssItem liClass)
@@ -1116,7 +1227,7 @@ public class SheetRenderer<T extends AbstractUISheet> extends RendererBase<T> {
     final String facet = action == SheetAction.toPage || action == SheetAction.toRow
         ? action.name() + "-" + target
         : action.name();
-    final AbstractUILink command = ensurePagingCommand(facesContext, data, facet, facet, disabled);
+    final AbstractUILink command = data.ensurePagingCommand(facesContext, data, facet, facet, disabled);
     if (target != null) {
       ComponentUtils.setAttribute(command, Attributes.pagingTarget, target);
     }
@@ -1148,7 +1259,7 @@ public class SheetRenderer<T extends AbstractUISheet> extends RendererBase<T> {
     writer.endElement(HtmlElements.BUTTON);
     writer.endElement(HtmlElements.LI);
   }
-
+*/
   // TODO sheet.getColumnLayout() may return the wrong number of column...
   // TODO
   // TODO
@@ -1161,12 +1272,12 @@ public class SheetRenderer<T extends AbstractUISheet> extends RendererBase<T> {
     writer.write("&nbsp;&nbsp;"); // is needed for IE
     writer.endElement(HtmlElements.SPAN);
   }
-
+/*
   private void encodeDirectPagingLinks(
       final FacesContext facesContext, final Application application, final AbstractUISheet sheet)
       throws IOException {
 
-    int linkCount = ComponentUtils.getIntAttribute(sheet, Attributes.directLinkCount);
+    int linkCount = sheet.getDirectLinkCount();
     linkCount--;  // current page needs no link
     final ArrayList<Integer> prevs = new ArrayList<>(linkCount);
     int page = sheet.getCurrentPage() + 1;
@@ -1234,53 +1345,5 @@ public class SheetRenderer<T extends AbstractUISheet> extends RendererBase<T> {
       encodeLink(facesContext, sheet, application, false, SheetAction.toPage, skip, Icons.THREE_DOTS, null);
     }
   }
-
-  private AbstractUILink ensurePagingCommand(
-      final FacesContext facesContext, final AbstractUISheet sheet, final String facet, final String id,
-      final boolean disabled) {
-
-    final Map<String, UIComponent> facets = sheet.getFacets();
-    AbstractUILink command = (AbstractUILink) facets.get(facet);
-    if (command == null) {
-      command = (AbstractUILink) ComponentUtils.createComponent(facesContext, Tags.link.componentType(),
-          RendererTypes.Link, SUFFIX_PAGE_ACTION + id);
-      command.setRendered(true);
-      command.setDisabled(disabled);
-      command.setTransient(true);
-      facets.put(facet, command);
-
-      // add AjaxBehavior
-      final AjaxBehavior behavior = createReloadBehavior(sheet);
-      command.addClientBehavior("click", behavior);
-    }
-    return command;
-  }
-
-  private AjaxBehavior createReloadBehavior(final AbstractUISheet sheet) {
-    final AjaxBehavior reloadBehavior = findReloadBehavior(sheet);
-    final ArrayList<String> renderIds = new ArrayList<>();
-    renderIds.add(sheet.getId());
-    if (reloadBehavior != null) {
-      renderIds.addAll(reloadBehavior.getRender());
-    }
-    final ArrayList<String> executeIds = new ArrayList<>();
-    executeIds.add(sheet.getId());
-    if (reloadBehavior != null) {
-      executeIds.addAll(reloadBehavior.getExecute());
-    }
-    final AjaxBehavior behavior = new AjaxBehavior();
-    behavior.setExecute(executeIds);
-    behavior.setRender(renderIds);
-    behavior.setTransient(true);
-    return behavior;
-  }
-
-  private AjaxBehavior findReloadBehavior(final ClientBehaviorHolder holder) {
-    final List<ClientBehavior> reload = holder.getClientBehaviors().get("reload");
-    if (reload != null && !reload.isEmpty() && reload.get(0) instanceof AjaxBehavior) {
-      return (AjaxBehavior) reload.get(0);
-    } else {
-      return null;
-    }
-  }
+ */
 }
diff --git a/tobago-core/src/main/java/org/apache/myfaces/tobago/internal/taglib/component/PaginatorListTagDeclaration.java b/tobago-core/src/main/java/org/apache/myfaces/tobago/internal/taglib/component/PaginatorListTagDeclaration.java
new file mode 100644
index 0000000000..cfe9ed5661
--- /dev/null
+++ b/tobago-core/src/main/java/org/apache/myfaces/tobago/internal/taglib/component/PaginatorListTagDeclaration.java
@@ -0,0 +1,58 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.myfaces.tobago.internal.taglib.component;
+
+import org.apache.myfaces.tobago.apt.annotation.Tag;
+import org.apache.myfaces.tobago.apt.annotation.TagAttribute;
+import org.apache.myfaces.tobago.apt.annotation.UIComponentTag;
+import org.apache.myfaces.tobago.apt.annotation.UIComponentTagAttribute;
+import org.apache.myfaces.tobago.component.RendererTypes;
+import org.apache.myfaces.tobago.internal.taglib.declaration.HasArrows;
+import org.apache.myfaces.tobago.internal.taglib.declaration.HasFor;
+import org.apache.myfaces.tobago.internal.taglib.declaration.HasIdBindingAndRendered;
+import org.apache.myfaces.tobago.internal.taglib.declaration.HasTip;
+import org.apache.myfaces.tobago.internal.taglib.declaration.IsAlwaysVisible;
+import org.apache.myfaces.tobago.internal.taglib.declaration.IsVisual;
+
+import javax.faces.component.UIOutput;
+
+/**
+ * Renders a paginator for direct links.
+ */
+@Tag(name = "paginatorList")
+@UIComponentTag(
+    uiComponent = "org.apache.myfaces.tobago.component.UIPaginatorList",
+    uiComponentFacesClass = "javax.faces.component.UIOutput",
+    componentFamily = UIOutput.COMPONENT_FAMILY,
+    rendererType = RendererTypes.PAGINATOR_LIST,
+    allowedChildComponenents = "NONE"
+)
+
+public interface PaginatorListTagDeclaration
+    extends HasIdBindingAndRendered, HasTip, IsVisual, IsAlwaysVisible, HasArrows, HasFor {
+
+  /**
+   * Show this amount of links, if available.
+   */
+  @TagAttribute
+  @UIComponentTagAttribute(
+      type = {"java.lang.Integer"})
+  void setMax(String max);
+}
diff --git a/tobago-core/src/main/java/org/apache/myfaces/tobago/internal/taglib/component/PaginatorPageTagDeclaration.java b/tobago-core/src/main/java/org/apache/myfaces/tobago/internal/taglib/component/PaginatorPageTagDeclaration.java
new file mode 100644
index 0000000000..b71f48917b
--- /dev/null
+++ b/tobago-core/src/main/java/org/apache/myfaces/tobago/internal/taglib/component/PaginatorPageTagDeclaration.java
@@ -0,0 +1,58 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.myfaces.tobago.internal.taglib.component;
+
+import org.apache.myfaces.tobago.apt.annotation.Tag;
+import org.apache.myfaces.tobago.apt.annotation.TagAttribute;
+import org.apache.myfaces.tobago.apt.annotation.UIComponentTag;
+import org.apache.myfaces.tobago.apt.annotation.UIComponentTagAttribute;
+import org.apache.myfaces.tobago.component.RendererTypes;
+import org.apache.myfaces.tobago.internal.taglib.declaration.HasArrows;
+import org.apache.myfaces.tobago.internal.taglib.declaration.HasFor;
+import org.apache.myfaces.tobago.internal.taglib.declaration.HasIdBindingAndRendered;
+import org.apache.myfaces.tobago.internal.taglib.declaration.HasTip;
+import org.apache.myfaces.tobago.internal.taglib.declaration.IsAlwaysVisible;
+import org.apache.myfaces.tobago.internal.taglib.declaration.IsVisual;
+
+import javax.faces.component.UIOutput;
+
+/**
+ * Renders a paginator to show and change the page number.
+ */
+@Tag(name = "paginatorPage")
+@UIComponentTag(
+    uiComponent = "org.apache.myfaces.tobago.component.UIPaginatorPage",
+    uiComponentFacesClass = "javax.faces.component.UIOutput",
+    componentFamily = UIOutput.COMPONENT_FAMILY,
+    rendererType = RendererTypes.PAGINATOR_PAGE,
+    allowedChildComponenents = "NONE"
+)
+
+public interface PaginatorPageTagDeclaration
+    extends HasIdBindingAndRendered, HasTip, IsVisual, IsAlwaysVisible, HasArrows, HasFor {
+
+  /**
+   * Show this amount of links, if available.
+   */
+  @TagAttribute
+  @UIComponentTagAttribute(
+      type = {"java.lang.Integer"})
+  void setMax(String max);
+}
diff --git a/tobago-core/src/main/java/org/apache/myfaces/tobago/internal/taglib/component/PaginatorPanelTagDeclaration.java b/tobago-core/src/main/java/org/apache/myfaces/tobago/internal/taglib/component/PaginatorPanelTagDeclaration.java
new file mode 100644
index 0000000000..f4f1a58968
--- /dev/null
+++ b/tobago-core/src/main/java/org/apache/myfaces/tobago/internal/taglib/component/PaginatorPanelTagDeclaration.java
@@ -0,0 +1,52 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.myfaces.tobago.internal.taglib.component;
+
+import org.apache.myfaces.tobago.apt.annotation.Tag;
+import org.apache.myfaces.tobago.apt.annotation.UIComponentTag;
+import org.apache.myfaces.tobago.component.RendererTypes;
+import org.apache.myfaces.tobago.internal.taglib.declaration.HasFor;
+import org.apache.myfaces.tobago.internal.taglib.declaration.HasIdBindingAndRendered;
+import org.apache.myfaces.tobago.internal.taglib.declaration.HasTip;
+import org.apache.myfaces.tobago.internal.taglib.declaration.IsAlwaysVisible;
+import org.apache.myfaces.tobago.internal.taglib.declaration.IsReadonly;
+import org.apache.myfaces.tobago.internal.taglib.declaration.IsVisual;
+
+import javax.faces.component.UIOutput;
+
+/**
+ * Renders a panel to put paginators in it.
+ */
+@Tag(name = "paginatorPanel")
+@UIComponentTag(
+    uiComponent = "org.apache.myfaces.tobago.component.UIPaginatorPanel",
+    uiComponentFacesClass = "javax.faces.component.UIOutput",
+    componentFamily = UIOutput.COMPONENT_FAMILY,
+    rendererType = RendererTypes.PAGINATOR_PANEL,
+    allowedChildComponenents = {
+        "org.apache.myfaces.tobago.PaginatorList",
+        "org.apache.myfaces.tobago.PaginatorPage",
+        "org.apache.myfaces.tobago.PaginatorRow"
+    }
+)
+
+public interface PaginatorPanelTagDeclaration
+    extends HasIdBindingAndRendered, HasTip, IsVisual, IsReadonly, IsAlwaysVisible, HasFor {
+}
diff --git a/tobago-core/src/main/java/org/apache/myfaces/tobago/internal/taglib/component/PaginatorRowTagDeclaration.java b/tobago-core/src/main/java/org/apache/myfaces/tobago/internal/taglib/component/PaginatorRowTagDeclaration.java
new file mode 100644
index 0000000000..a35e05534b
--- /dev/null
+++ b/tobago-core/src/main/java/org/apache/myfaces/tobago/internal/taglib/component/PaginatorRowTagDeclaration.java
@@ -0,0 +1,48 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.myfaces.tobago.internal.taglib.component;
+
+import org.apache.myfaces.tobago.apt.annotation.Tag;
+import org.apache.myfaces.tobago.apt.annotation.UIComponentTag;
+import org.apache.myfaces.tobago.component.RendererTypes;
+import org.apache.myfaces.tobago.internal.taglib.declaration.HasFor;
+import org.apache.myfaces.tobago.internal.taglib.declaration.HasIdBindingAndRendered;
+import org.apache.myfaces.tobago.internal.taglib.declaration.HasTip;
+import org.apache.myfaces.tobago.internal.taglib.declaration.IsAlwaysVisible;
+import org.apache.myfaces.tobago.internal.taglib.declaration.IsReadonly;
+import org.apache.myfaces.tobago.internal.taglib.declaration.IsVisual;
+
+import javax.faces.component.UIOutput;
+
+/**
+ * Renders a paginator to show and change the row position.
+ */
+@Tag(name = "paginatorRow")
+@UIComponentTag(
+    uiComponent = "org.apache.myfaces.tobago.component.UIPaginatorRow",
+    uiComponentFacesClass = "javax.faces.component.UIOutput",
+    componentFamily = UIOutput.COMPONENT_FAMILY,
+    rendererType = RendererTypes.PAGINATOR_ROW,
+    allowedChildComponenents = "NONE"
+)
+
+public interface PaginatorRowTagDeclaration
+    extends HasIdBindingAndRendered, HasTip, IsVisual, IsReadonly, IsAlwaysVisible, HasFor {
+}
diff --git a/tobago-core/src/main/java/org/apache/myfaces/tobago/internal/taglib/component/SheetTagDeclaration.java b/tobago-core/src/main/java/org/apache/myfaces/tobago/internal/taglib/component/SheetTagDeclaration.java
index e5b646aea7..a2b851facb 100644
--- a/tobago-core/src/main/java/org/apache/myfaces/tobago/internal/taglib/component/SheetTagDeclaration.java
+++ b/tobago-core/src/main/java/org/apache/myfaces/tobago/internal/taglib/component/SheetTagDeclaration.java
@@ -36,6 +36,7 @@ import org.apache.myfaces.tobago.internal.taglib.declaration.HasVar;
 import org.apache.myfaces.tobago.internal.taglib.declaration.IsShowRoot;
 import org.apache.myfaces.tobago.internal.taglib.declaration.IsShowRootJunction;
 import org.apache.myfaces.tobago.internal.taglib.declaration.IsVisual;
+import org.apache.myfaces.tobago.layout.PaginatorMode;
 import org.apache.myfaces.tobago.layout.ShowPosition;
 import org.apache.myfaces.tobago.model.Selectable;
 
@@ -58,7 +59,14 @@ import javax.faces.component.UIData;
         @Facet(
             name = Facets.RELOAD,
             description = "Contains an instance of UIReload",
-            allowedChildComponenents = "org.apache.myfaces.tobago.Reload")},
+            allowedChildComponenents = "org.apache.myfaces.tobago.Reload"),
+        @Facet(
+            name = Facets.BEFORE,
+            description = "Components to be rendered before the sheet, e.g. paginators"),
+        @Facet(
+            name = Facets.AFTER,
+            description = "Components to be rendered after the sheet, e.g. paginators")
+    },
     behaviors = {
         @Behavior(
             name = ClientBehaviors.RELOAD, // XXX replace by click
@@ -154,14 +162,20 @@ public interface SheetTagDeclaration
    * </ul>
    * <li>showPagingAlways="true" means, that the paging footer should be displayed in any case.
    * </ul>
+   *
+   * @deprecated Use paginator attribute or paginator tags instead.
    */
+  @Deprecated(since = "5.14.0, 6.6.0", forRemoval = true)
   @TagAttribute
   @UIComponentTagAttribute(type = "boolean", defaultValue = "false")
   void setShowPagingAlways(String showPagingAlways);
 
   /**
-   * The count of rendered direct paging links in the sheet's footer.<br>
+   * The count of rendered direct paging links in the sheet's footer.
+   *
+   * @deprecated Use paginator attribute or paginator tags instead.
    */
+  @Deprecated(since = "5.14.0, 6.6.0", forRemoval = true)
   @TagAttribute
   @UIComponentTagAttribute(type = "java.lang.Integer", defaultValue = "9")
   void setDirectLinkCount(String directLinkCount);
@@ -169,7 +183,10 @@ public interface SheetTagDeclaration
   /**
    * Flag indicating whether or not a range of direct paging links should be
    * rendered in the sheet's footer.
+   *
+   * @deprecated Use paginator attribute or paginator tags instead.
    */
+  @Deprecated(since = "5.14.0, 6.6.0", forRemoval = true)
   @TagAttribute
   @UIComponentTagAttribute(
       type = "org.apache.myfaces.tobago.layout.ShowPosition",
@@ -184,7 +201,10 @@ public interface SheetTagDeclaration
    * Flag indicating whether and where the range pages should
    * rendered in the sheet's footer. Rendering this range also offers the
    * capability to enter the index displayed page directly.
+   *
+   * @deprecated Use paginator attribute or paginator tags instead.
    */
+  @Deprecated(since = "5.14.0, 6.6.0", forRemoval = true)
   @TagAttribute
   @UIComponentTagAttribute(
       type = "org.apache.myfaces.tobago.layout.ShowPosition",
@@ -199,7 +219,10 @@ public interface SheetTagDeclaration
    * Flag indicating whether or not the range of displayed rows should
    * rendered in the sheet's footer. Rendering this range also offers the
    * capability to enter the index of the start row directly.
+   *
+   * @deprecated Use paginator attribute or paginator tags instead.
    */
+  @Deprecated(since = "5.14.0, 6.6.0", forRemoval = true)
   @TagAttribute
   @UIComponentTagAttribute(
       type = "org.apache.myfaces.tobago.layout.ShowPosition",
@@ -276,13 +299,15 @@ public interface SheetTagDeclaration
    */
   @TagAttribute
   @UIComponentTagAttribute(type = "java.lang.Integer", defaultValue = "50")
-  void setLazyRows(String lazy);
+  void setLazyRows(String lazyRows);
 
   /**
    * Flag indicating if paging arrows are shown near direct links
    *
    * @since 2.0.0
+   * @deprecated Use paginator attribute or paginator tags instead.
    */
+  @Deprecated(since = "5.14.0, 6.6.0", forRemoval = true)
   @TagAttribute
   @UIComponentTagAttribute(type = "boolean", defaultValue = "false")
   void setShowDirectLinksArrows(String showDirectLinksArrows);
@@ -291,7 +316,9 @@ public interface SheetTagDeclaration
    * Flag indicating if paging arrows are shown near page range
    *
    * @since 2.0.0
+   * @deprecated Use paginator attribute or paginator tags instead.
    */
+  @Deprecated(since = "5.14.0, 6.6.0", forRemoval = true)
   @TagAttribute
   @UIComponentTagAttribute(type = "boolean", defaultValue = "true")
   void setShowPageRangeArrows(String showPageRangeArrows);
@@ -305,4 +332,52 @@ public interface SheetTagDeclaration
   @UIComponentTagAttribute(type = "java.lang.Integer", defaultValue = "0")
   void setMaxSortColumns(String maxSortColumns);
 
+  /**
+   * Create paginator components automatically or do it manually.
+   * <ul>
+   *   <li>
+   *      <b>custom</b>: no paginator is created automatically, you have to do it manually.
+   *      This can be done
+   *      using a <code>&lt;tc:paginatorPanel&gt;</code>
+   *      with e.g. a <code>&lt;tc:paginatorList&gt;</code> and/or some other
+   *      paginator components.
+   *   </li>
+   *   <li>
+   *     <b>auto</b>: a appropriate paginator is used.
+   *   </li>
+   *   <li>
+   *     <b>list</b>: a <code>&lt;tc:paginatorList&gt;</code> is used.
+   *   </li>
+   *   <li>
+   *     <b>page</b>: a <code>&lt;tc:paginatorPage&gt;</code> is used.
+   *   </li>
+   *   <li>
+   *     <b>row</b>: a <code>&lt;tc:paginatorRow&gt;</code> is used.
+   *   </li>
+   *   <li>
+   *     <b>useShowAttributes</b> (default):
+   *     is deprecated, use for compatibility.
+   *     The used paginators are defined by the show-attributes:
+   *     <code>showDirectLinks</code>,
+   *     <code>showPageRange</code>,
+   *     <code>showRowRange</code>,
+   *     <code>directLinkCount</code>,
+   *     <code>showDirectLinksArrows</code>,
+   *     <code>showPageRangeArrows</code>,
+   *     <code>showPagingAlways</code>.
+   *   </li>
+   * </ul>
+   *
+   * @since 5.14.0, 6.6.0
+   */
+  @TagAttribute
+  @UIComponentTagAttribute(
+      type = "org.apache.myfaces.tobago.layout.PaginatorMode",
+      defaultValue = PaginatorMode.USE_SHOW_ATTRIBUTES,
+      allowedValues = {
+          PaginatorMode.AUTO, PaginatorMode.CUSTOM, PaginatorMode.LIST, PaginatorMode.PAGE, PaginatorMode.ROW,
+          PaginatorMode.USE_SHOW_ATTRIBUTES
+      },
+      defaultCode = "org.apache.myfaces.tobago.layout.PaginatorMode.useShowAttributes")
+  void setPaginator(String paginator);
 }
diff --git a/tobago-core/src/main/java/org/apache/myfaces/tobago/internal/taglib/declaration/HasArrows.java b/tobago-core/src/main/java/org/apache/myfaces/tobago/internal/taglib/declaration/HasArrows.java
new file mode 100644
index 0000000000..618dab48a8
--- /dev/null
+++ b/tobago-core/src/main/java/org/apache/myfaces/tobago/internal/taglib/declaration/HasArrows.java
@@ -0,0 +1,39 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.myfaces.tobago.internal.taglib.declaration;
+
+import org.apache.myfaces.tobago.apt.annotation.TagAttribute;
+import org.apache.myfaces.tobago.apt.annotation.UIComponentTagAttribute;
+import org.apache.myfaces.tobago.layout.Arrows;
+
+public interface HasArrows {
+  /**
+   * Show arrows at this control.
+   */
+  @TagAttribute
+  @UIComponentTagAttribute(
+      type = "org.apache.myfaces.tobago.layout.Arrows",
+      defaultValue = Arrows.AUTO,
+      allowedValues = {
+          Arrows.AUTO, Arrows.SHOW, Arrows.HIDE
+      },
+      defaultCode = "org.apache.myfaces.tobago.layout.Arrows.auto")
+  void setArrows(String arrows);
+}
diff --git a/tobago-core/src/main/java/org/apache/myfaces/tobago/internal/taglib/declaration/IsAlwaysVisible.java b/tobago-core/src/main/java/org/apache/myfaces/tobago/internal/taglib/declaration/IsAlwaysVisible.java
new file mode 100644
index 0000000000..8a72daa13e
--- /dev/null
+++ b/tobago-core/src/main/java/org/apache/myfaces/tobago/internal/taglib/declaration/IsAlwaysVisible.java
@@ -0,0 +1,33 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.myfaces.tobago.internal.taglib.declaration;
+
+import org.apache.myfaces.tobago.apt.annotation.TagAttribute;
+import org.apache.myfaces.tobago.apt.annotation.UIComponentTagAttribute;
+
+public interface IsAlwaysVisible {
+  /**
+   * Flag indicating that the control show should be displayed in call cases,
+   * whether it is necessary or not (if there are a low number of rows).
+   */
+  @TagAttribute
+  @UIComponentTagAttribute(type = "boolean", defaultValue = "false")
+  void setAlwaysVisible(String alwaysVisible);
+}
diff --git a/tobago-core/src/main/java/org/apache/myfaces/tobago/layout/Arrows.java b/tobago-core/src/main/java/org/apache/myfaces/tobago/layout/Arrows.java
new file mode 100644
index 0000000000..b637a09f5d
--- /dev/null
+++ b/tobago-core/src/main/java/org/apache/myfaces/tobago/layout/Arrows.java
@@ -0,0 +1,61 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.myfaces.tobago.layout;
+
+/**
+ * Show Arrows, or not.
+ *
+ * @since 5.14.0, 6.6.0
+ */
+public enum Arrows {
+
+  /**
+   * Show arrows at this control.
+   */
+  show,
+
+  /**
+   * Hide arrows at this control.
+   */
+  hide,
+
+  /**
+   * Arrows will be shown automatically (currently the same as {@link #show}).
+   */
+  auto;
+
+  public static final String SHOW = "show";
+  public static final String HIDE = "hide";
+  public static final String AUTO = "auto";
+
+  /**
+   * @param name Name of the Arrows.
+   * @throws IllegalArgumentException When the name doesn't match any {@link Arrows}.
+   */
+  public static Arrows parse(final Object name) throws IllegalArgumentException {
+    if (name == null) {
+      return null;
+    }
+    if (name instanceof Arrows) {
+      return (Arrows) name;
+    }
+    return valueOf(name.toString());
+  }
+}
diff --git a/tobago-core/src/main/java/org/apache/myfaces/tobago/layout/PaginatorMode.java b/tobago-core/src/main/java/org/apache/myfaces/tobago/layout/PaginatorMode.java
new file mode 100644
index 0000000000..347fc7e597
--- /dev/null
+++ b/tobago-core/src/main/java/org/apache/myfaces/tobago/layout/PaginatorMode.java
@@ -0,0 +1,86 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.myfaces.tobago.layout;
+
+/**
+ * Describes the mode of automatically displayed paginator.
+ *
+ * @since 5.14.0, 6.6.0
+ */
+public enum PaginatorMode {
+
+  /**
+   * A paginator is selected and displayed automatically.
+   * Will be the default in future versions.
+   */
+  auto,
+
+  /**
+   * If a paginator is desired, it must be specified on the page, e.g. with a
+   * <code>&lt;tc:paginatorList&gt;</code> inside of a <code>&lt;tc:paginatorPanel&gt;</code> tag.
+   */
+  custom,
+
+  /**
+   * The paginator is displayed as a list.
+   */
+  list,
+
+  /**
+   * The page can be selected directly.
+   */
+  page,
+
+  /**
+   * The row can be selected directly.
+   */
+  row,
+
+  /**
+   * The show attributes of the sheet (e.g. showDirectLinks, showPageRange, showPageLinks, showRowCount)
+   * are used to display the paginator. This is the default, and emulates the old behavior.
+   *
+   * @deprecated Please use {@link #auto} if possible, or write a custom paginator.
+   */
+  @Deprecated(since = "5.14.0, 6.6.0", forRemoval = true)
+  useShowAttributes;
+
+  public static final String AUTO = "auto";
+  public static final String CUSTOM = "custom";
+  public static final String LIST = "list";
+  public static final String PAGE = "page";
+  public static final String ROW = "row";
+  @Deprecated(since = "5.14.0, 6.6.0", forRemoval = true)
+  public static final String USE_SHOW_ATTRIBUTES = "useShowAttributes";
+
+  /**
+   * @param name Name of the PaginatorMode.
+   * @throws IllegalArgumentException When the name doesn't match any {@link PaginatorMode}.
+   */
+  public static PaginatorMode parse(final Object name) throws IllegalArgumentException {
+    if (name == null) {
+      return null;
+    }
+    if (name instanceof PaginatorMode) {
+      return (PaginatorMode) name;
+    }
+    return valueOf(name.toString());
+  }
+}
diff --git a/tobago-core/src/main/java/org/apache/myfaces/tobago/renderkit/css/BootstrapClass.java b/tobago-core/src/main/java/org/apache/myfaces/tobago/renderkit/css/BootstrapClass.java
index a898b9372a..9fba1ebd11 100644
--- a/tobago-core/src/main/java/org/apache/myfaces/tobago/renderkit/css/BootstrapClass.java
+++ b/tobago-core/src/main/java/org/apache/myfaces/tobago/renderkit/css/BootstrapClass.java
@@ -605,6 +605,7 @@ public enum BootstrapClass implements CssItem {
   TEXT_DARK("text-dark"),
   TEXT_INFO("text-info"),
   TEXT_LIGHT("text-light"),
+  TEXT_NOWRAP("text-nowrap"),
   TEXT_PRIMARY("text-primary"),
   TEXT_SECONDARY("text-secondary"),
   TEXT_SUCCESS("text-success"),
diff --git a/tobago-core/src/main/java/org/apache/myfaces/tobago/renderkit/html/HtmlElements.java b/tobago-core/src/main/java/org/apache/myfaces/tobago/renderkit/html/HtmlElements.java
index 17d7d46557..d2edee378f 100644
--- a/tobago-core/src/main/java/org/apache/myfaces/tobago/renderkit/html/HtmlElements.java
+++ b/tobago-core/src/main/java/org/apache/myfaces/tobago/renderkit/html/HtmlElements.java
@@ -157,6 +157,10 @@ public enum HtmlElements {
   TOBAGO_MESSAGES("tobago-messages"),
   TOBAGO_OUT("tobago-out", Qualifier.INLINE),
   TOBAGO_PAGE("tobago-page"),
+  TOBAGO_PAGINATOR_LIST("tobago-paginator-list"),
+  TOBAGO_PAGINATOR_PAGE("tobago-paginator-page"),
+  TOBAGO_PAGINATOR_PANEL("tobago-paginator-panel"),
+  TOBAGO_PAGINATOR_ROW("tobago-paginator-row"),
   TOBAGO_PANEL("tobago-panel"),
   TOBAGO_POPOVER("tobago-popover"),
   TOBAGO_POPUP("tobago-popup"),
diff --git a/tobago-core/src/test/java/org/apache/myfaces/tobago/internal/config/AbstractTobagoTestBase.java b/tobago-core/src/test/java/org/apache/myfaces/tobago/internal/config/AbstractTobagoTestBase.java
index 5f714a278b..60aff94874 100644
--- a/tobago-core/src/test/java/org/apache/myfaces/tobago/internal/config/AbstractTobagoTestBase.java
+++ b/tobago-core/src/test/java/org/apache/myfaces/tobago/internal/config/AbstractTobagoTestBase.java
@@ -40,6 +40,10 @@ import org.apache.myfaces.tobago.component.UILink;
 import org.apache.myfaces.tobago.component.UILinks;
 import org.apache.myfaces.tobago.component.UIOut;
 import org.apache.myfaces.tobago.component.UIPage;
+import org.apache.myfaces.tobago.component.UIPaginatorList;
+import org.apache.myfaces.tobago.component.UIPaginatorPage;
+import org.apache.myfaces.tobago.component.UIPaginatorPanel;
+import org.apache.myfaces.tobago.component.UIPaginatorRow;
 import org.apache.myfaces.tobago.component.UIPanel;
 import org.apache.myfaces.tobago.component.UIPopup;
 import org.apache.myfaces.tobago.component.UIRange;
@@ -69,6 +73,7 @@ import org.apache.myfaces.tobago.component.UITreeListbox;
 import org.apache.myfaces.tobago.component.UITreeNode;
 import org.apache.myfaces.tobago.component.UITreeSelect;
 import org.apache.myfaces.tobago.config.TobagoConfig;
+import static org.apache.myfaces.tobago.config.TobagoConfig.TOBAGO_CONFIG;
 import org.apache.myfaces.tobago.context.TobagoContext;
 import org.apache.myfaces.tobago.internal.behavior.EventBehavior;
 import org.apache.myfaces.tobago.internal.renderkit.renderer.BadgeRenderer;
@@ -86,6 +91,10 @@ import org.apache.myfaces.tobago.internal.renderkit.renderer.LinkRenderer;
 import org.apache.myfaces.tobago.internal.renderkit.renderer.LinksRenderer;
 import org.apache.myfaces.tobago.internal.renderkit.renderer.OutRenderer;
 import org.apache.myfaces.tobago.internal.renderkit.renderer.PageRenderer;
+import org.apache.myfaces.tobago.internal.renderkit.renderer.PaginatorListRenderer;
+import org.apache.myfaces.tobago.internal.renderkit.renderer.PaginatorPageRenderer;
+import org.apache.myfaces.tobago.internal.renderkit.renderer.PaginatorPanelRenderer;
+import org.apache.myfaces.tobago.internal.renderkit.renderer.PaginatorRowRenderer;
 import org.apache.myfaces.tobago.internal.renderkit.renderer.PanelRenderer;
 import org.apache.myfaces.tobago.internal.renderkit.renderer.PopupRenderer;
 import org.apache.myfaces.tobago.internal.renderkit.renderer.RangeRenderer;
@@ -115,6 +124,7 @@ import org.apache.myfaces.tobago.internal.renderkit.renderer.TreeNodeRenderer;
 import org.apache.myfaces.tobago.internal.renderkit.renderer.TreeRenderer;
 import org.apache.myfaces.tobago.internal.renderkit.renderer.TreeSelectRenderer;
 import org.apache.myfaces.tobago.internal.webapp.HtmlResponseWriter;
+import static org.apache.myfaces.tobago.util.ResourceUtils.TOBAGO_RESOURCE_BUNDLE;
 import org.junit.jupiter.api.AfterEach;
 import org.junit.jupiter.api.BeforeEach;
 
@@ -129,9 +139,6 @@ import java.nio.charset.StandardCharsets;
 import java.util.Date;
 import java.util.Locale;
 
-import static org.apache.myfaces.tobago.config.TobagoConfig.TOBAGO_CONFIG;
-import static org.apache.myfaces.tobago.util.ResourceUtils.TOBAGO_RESOURCE_BUNDLE;
-
 /**
  * <p>Abstract JUnit test case base class, which sets up the JavaServer Faces
  * mock object environment for a particular simulated request.
@@ -185,6 +192,10 @@ public abstract class AbstractTobagoTestBase extends AbstractJsfTestCase {
     application.addComponent(Tags.links.componentType(), UILinks.class.getName());
     application.addComponent(Tags.out.componentType(), UIOut.class.getName());
     application.addComponent(Tags.page.componentType(), UIPage.class.getName());
+    application.addComponent(Tags.paginatorList.componentType(), UIPaginatorList.class.getName());
+    application.addComponent(Tags.paginatorPage.componentType(), UIPaginatorPage.class.getName());
+    application.addComponent(Tags.paginatorPanel.componentType(), UIPaginatorPanel.class.getName());
+    application.addComponent(Tags.paginatorRow.componentType(), UIPaginatorRow.class.getName());
     application.addComponent(Tags.panel.componentType(), UIPanel.class.getName());
     application.addComponent(Tags.popup.componentType(), UIPopup.class.getName());
     application.addComponent(Tags.range.componentType(), UIRange.class.getName());
@@ -238,6 +249,14 @@ public abstract class AbstractTobagoTestBase extends AbstractJsfTestCase {
     renderKit.addRenderer(UILinks.COMPONENT_FAMILY, RendererTypes.LINKS, new LinksRenderer<UILinks>());
     renderKit.addRenderer(UIOut.COMPONENT_FAMILY, RendererTypes.OUT, new OutRenderer<UIOut>());
     renderKit.addRenderer(UIPage.COMPONENT_FAMILY, RendererTypes.PAGE, new PageRenderer<UIPage>());
+    renderKit.addRenderer(UIPaginatorList.COMPONENT_FAMILY, RendererTypes.PAGINATOR_LIST,
+        new PaginatorListRenderer<UIPaginatorList>());
+    renderKit.addRenderer(UIPaginatorPage.COMPONENT_FAMILY, RendererTypes.PAGINATOR_PAGE,
+        new PaginatorPageRenderer<UIPaginatorPage>());
+    renderKit.addRenderer(UIPaginatorPanel.COMPONENT_FAMILY, RendererTypes.PAGINATOR_PANEL,
+        new PaginatorPanelRenderer<UIPaginatorPanel>());
+    renderKit.addRenderer(UIPaginatorRow.COMPONENT_FAMILY, RendererTypes.PAGINATOR_ROW,
+        new PaginatorRowRenderer<UIPaginatorRow>());
     renderKit.addRenderer(UIPanel.COMPONENT_FAMILY, RendererTypes.PANEL, new PanelRenderer<UIPanel>());
     renderKit.addRenderer(UIPopup.COMPONENT_FAMILY, RendererTypes.POPUP, new PopupRenderer<UIPopup>());
     renderKit.addRenderer(UIRange.COMPONENT_FAMILY, RendererTypes.RANGE, new RangeRenderer<UIRange>());
diff --git a/tobago-core/src/test/java/org/apache/myfaces/tobago/internal/renderkit/renderer/SheetRendererUnitTest.java b/tobago-core/src/test/java/org/apache/myfaces/tobago/internal/renderkit/renderer/SheetRendererUnitTest.java
index 61d48616e2..0b2b7f7dce 100644
--- a/tobago-core/src/test/java/org/apache/myfaces/tobago/internal/renderkit/renderer/SheetRendererUnitTest.java
+++ b/tobago-core/src/test/java/org/apache/myfaces/tobago/internal/renderkit/renderer/SheetRendererUnitTest.java
@@ -25,11 +25,15 @@ import org.apache.myfaces.tobago.component.RendererTypes;
 import org.apache.myfaces.tobago.component.Tags;
 import org.apache.myfaces.tobago.component.UIColumn;
 import org.apache.myfaces.tobago.component.UIOut;
+import org.apache.myfaces.tobago.component.UIPaginatorList;
 import org.apache.myfaces.tobago.component.UISheet;
+import org.apache.myfaces.tobago.layout.Arrows;
+import org.apache.myfaces.tobago.layout.PaginatorMode;
 import org.apache.myfaces.tobago.util.ComponentUtils;
 import org.junit.jupiter.api.Assertions;
 import org.junit.jupiter.api.Test;
 
+import javax.faces.component.UIComponent;
 import java.io.IOException;
 
 public class SheetRendererUnitTest extends RendererTestBase {
@@ -104,4 +108,38 @@ public class SheetRendererUnitTest extends RendererTestBase {
 
     Assertions.assertEquals(loadHtml("renderer/sheet/sheet-paging.html"), formattedResult());
   }
+
+  @Test
+  public void sheetPaginatorList() throws IOException {
+    final UISheet sheet = (UISheet) ComponentUtils.createComponent(
+        facesContext, Tags.sheet.componentType(), RendererTypes.Sheet, "sheet");
+    sheet.setVar("item");
+    sheet.setValue(getSheetSample(10));
+    sheet.setRows(2);
+    sheet.setPaginator(PaginatorMode.custom);
+
+    final UIColumn column = (UIColumn) ComponentUtils.createComponent(
+        facesContext, Tags.column.componentType(), RendererTypes.Column, "column");
+    column.setLabel("Alphabet");
+    sheet.getChildren().add(column);
+
+    final UIOut out = (UIOut) ComponentUtils.createComponent(
+        facesContext, Tags.out.componentType(), RendererTypes.Out, "out");
+    out.setValueExpression("value", new MockValueExpression("#{item.name}", String.class));
+    out.setLabelLayout(LabelLayout.skip);
+    column.getChildren().add(out);
+
+    final UIComponent panel = ComponentUtils.createComponent(
+        facesContext, Tags.paginatorPanel.componentType(), RendererTypes.PaginatorPanel, "panel");
+    sheet.getFacets().put("after", panel);
+
+    final UIPaginatorList paginatorList = (UIPaginatorList) ComponentUtils.createComponent(
+        facesContext, Tags.paginatorList.componentType(), RendererTypes.PaginatorList, "list");
+    paginatorList.setArrows(Arrows.hide);
+    panel.getChildren().add(paginatorList);
+
+    sheet.encodeAll(facesContext);
+
+    Assertions.assertEquals(loadHtml("renderer/sheet/sheet-paginatorList.html"), formattedResult());
+  }
 }
diff --git a/tobago-core/src/test/java/org/apache/myfaces/tobago/layout/ArrowsUnitTest.java b/tobago-core/src/test/java/org/apache/myfaces/tobago/layout/ArrowsUnitTest.java
new file mode 100644
index 0000000000..70cc20e8ed
--- /dev/null
+++ b/tobago-core/src/test/java/org/apache/myfaces/tobago/layout/ArrowsUnitTest.java
@@ -0,0 +1,31 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.myfaces.tobago.layout;
+
+import org.apache.myfaces.tobago.util.EnumUnitTest;
+import org.junit.jupiter.api.Test;
+
+public class ArrowsUnitTest extends EnumUnitTest {
+
+  @Test
+  public void testNames() throws IllegalAccessException, NoSuchFieldException {
+    testNames(Arrows.class);
+  }
+}
diff --git a/tobago-core/src/test/java/org/apache/myfaces/tobago/layout/PaginatorModeUnitTest.java b/tobago-core/src/test/java/org/apache/myfaces/tobago/layout/PaginatorModeUnitTest.java
new file mode 100644
index 0000000000..bead6d84ef
--- /dev/null
+++ b/tobago-core/src/test/java/org/apache/myfaces/tobago/layout/PaginatorModeUnitTest.java
@@ -0,0 +1,31 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.myfaces.tobago.layout;
+
+import org.apache.myfaces.tobago.util.EnumUnitTest;
+import org.junit.jupiter.api.Test;
+
+public class PaginatorModeUnitTest extends EnumUnitTest {
+
+  @Test
+  public void testNames() throws IllegalAccessException, NoSuchFieldException {
+    testNames(PaginatorMode.class);
+  }
+}
diff --git a/tobago-core/src/test/resources/renderer/sheet/sheet-paginatorList.html b/tobago-core/src/test/resources/renderer/sheet/sheet-paginatorList.html
new file mode 100644
index 0000000000..d90f1b3215
--- /dev/null
+++ b/tobago-core/src/test/resources/renderer/sheet/sheet-paginatorList.html
@@ -0,0 +1,71 @@
+<!--
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+-->
+
+<tobago-sheet id='sheet' data-tobago-selection-mode='multi' data-tobago-first='0' rows='2' row-count='10'>
+  <input id='sheet::scrollPosition' name='sheet::scrollPosition' type='hidden' value='[0,0]'>
+  <input id='sheet::selected' name='sheet::selected' type='hidden' value='[]'>
+  <div class='tobago-body'>
+    <table cellspacing='0' cellpadding='0' summary='' class='table'>
+      <thead>
+        <tr>
+          <th>
+            <span><tobago-out id='sheet:_col0'><span class='form-control-plaintext'>Alphabet</span></tobago-out></span>
+          </th>
+        </tr>
+      </thead>
+      <tbody>
+        <tr row-index='0'>
+          <td>
+            <tobago-out id='sheet:0:out'><span class='form-control-plaintext'>Name 0</span></tobago-out>
+          </td>
+          <td class='tobago-behavior-container'>
+          </td>
+        </tr>
+        <tr row-index='1'>
+          <td>
+            <tobago-out id='sheet:1:out'><span class='form-control-plaintext'>Name 1</span></tobago-out>
+          </td>
+          <td class='tobago-behavior-container'>
+          </td>
+        </tr>
+      </tbody>
+    </table>
+  </div>
+  <footer>
+  </footer>
+  <tobago-paginator-panel id='sheet:panel' class='d-flex justify-content-center'>
+    <tobago-paginator-list id='sheet:list'>
+      <ul class='pagination'>
+        <li class='active page-item'>
+          <button type='button' class='page-link' id='sheet:pageActiontoPage-1' title='Page 1'>1<tobago-behavior event='click' client-id='sheet:pageActiontoPage-1' execute='sheet sheet:pageActiontoPage-1' render='sheet'></tobago-behavior></button>
+        </li>
+        <li class='page-item'>
+          <button type='button' class='page-link' id='sheet:pageActiontoPage-2' title='Page 2'>2<tobago-behavior event='click' client-id='sheet:pageActiontoPage-2' execute='sheet sheet:pageActiontoPage-2' render='sheet'></tobago-behavior></button>
+        </li>
+        <li class='page-item'>
+          <button type='button' class='page-link' id='sheet:pageActiontoPage-3' title='Page 3'>3<tobago-behavior event='click' client-id='sheet:pageActiontoPage-3' execute='sheet sheet:pageActiontoPage-3' render='sheet'></tobago-behavior></button>
+        </li>
+        <li class='page-item'>
+          <button type='button' class='page-link' id='sheet:pageActiontoPage-4' title='Page 4'>4<tobago-behavior event='click' client-id='sheet:pageActiontoPage-4' execute='sheet sheet:pageActiontoPage-4' render='sheet'></tobago-behavior></button>
+        </li>
+        <li class='page-item'>
+          <button type='button' class='page-link' id='sheet:pageActiontoPage-5' title='Page 5'>5<tobago-behavior event='click' client-id='sheet:pageActiontoPage-5' execute='sheet sheet:pageActiontoPage-5' render='sheet'></tobago-behavior></button>
+        </li>
+      </ul>
+    </tobago-paginator-list>
+  </tobago-paginator-panel>
+</tobago-sheet>
diff --git a/tobago-core/src/test/resources/renderer/sheet/sheet-paging.html b/tobago-core/src/test/resources/renderer/sheet/sheet-paging.html
index b3b08b874e..bc71bf407d 100644
--- a/tobago-core/src/test/resources/renderer/sheet/sheet-paging.html
+++ b/tobago-core/src/test/resources/renderer/sheet/sheet-paging.html
@@ -46,29 +46,31 @@
     </table>
   </div>
   <footer>
-    <ul class='me-auto pagination'>
-      <li class='page-item' title='Click here, to change the first displayed row'>
-        <span class='tobago-paging page-link'>Rows <span>1</span><input id='sheet:pageActiontoRow' name='sheet:pageActiontoRow' type='text' value='1' maxlength='2' pattern='[1-9][0-9]?'> to 2 of 10</span>
-      </li>
-    </ul>
-    <ul class='mx-auto pagination'>
-      <li class='active page-item'>
-        <button type='button' class='page-link' id='sheet:pageActiontoPage-1' title='Page 1'>1<tobago-behavior event='click' client-id='sheet:pageActiontoPage-1' execute='sheet sheet:pageActiontoPage-1' render='sheet'></tobago-behavior></button>
-      </li>
-      <li class='page-item'>
-        <button type='button' class='page-link' id='sheet:pageActiontoPage-2' title='Page 2'>2<tobago-behavior event='click' client-id='sheet:pageActiontoPage-2' execute='sheet sheet:pageActiontoPage-2' render='sheet'></tobago-behavior></button>
-      </li>
-      <li class='page-item'>
-        <button type='button' class='page-link' id='sheet:pageActiontoPage-3' title='Page 3'>3<tobago-behavior event='click' client-id='sheet:pageActiontoPage-3' execute='sheet sheet:pageActiontoPage-3' render='sheet'></tobago-behavior></button>
-      </li>
-      <li class='page-item'>
-        <button type='button' class='page-link' id='sheet:pageActiontoPage-4' title='Page 4'>4<tobago-behavior event='click' client-id='sheet:pageActiontoPage-4' execute='sheet sheet:pageActiontoPage-4' render='sheet'></tobago-behavior></button>
-      </li>
-      <li class='page-item'>
-        <button type='button' class='page-link' id='sheet:pageActiontoPage-5' title='Page 5'>5<tobago-behavior event='click' client-id='sheet:pageActiontoPage-5' execute='sheet sheet:pageActiontoPage-5' render='sheet'></tobago-behavior></button>
-      </li>
-    </ul>
-    <ul class='ms-auto pagination'>
+  </footer>
+  <tobago-paginator-panel id='sheet:_after' class='d-flex justify-content-between'>
+    <tobago-paginator-row id='sheet:_paginator_row' class='pagination'>
+      <span class='page-item' title='Click here, to change the first displayed row'><span class='page-link text-nowrap'>Rows <span>1</span><input id='sheet:pageActiontoRow' name='sheet:pageActiontoRow' type='text' value='1' maxlength='2' pattern='[1-9][0-9]?'> to 2 of 10</span></span>
+    </tobago-paginator-row>
+    <tobago-paginator-list id='sheet:_paginator_list'>
+      <ul class='pagination'>
+        <li class='active page-item'>
+          <button type='button' class='page-link' id='sheet:pageActiontoPage-1' title='Page 1'>1<tobago-behavior event='click' client-id='sheet:pageActiontoPage-1' execute='sheet sheet:pageActiontoPage-1' render='sheet'></tobago-behavior></button>
+        </li>
+        <li class='page-item'>
+          <button type='button' class='page-link' id='sheet:pageActiontoPage-2' title='Page 2'>2<tobago-behavior event='click' client-id='sheet:pageActiontoPage-2' execute='sheet sheet:pageActiontoPage-2' render='sheet'></tobago-behavior></button>
+        </li>
+        <li class='page-item'>
+          <button type='button' class='page-link' id='sheet:pageActiontoPage-3' title='Page 3'>3<tobago-behavior event='click' client-id='sheet:pageActiontoPage-3' execute='sheet sheet:pageActiontoPage-3' render='sheet'></tobago-behavior></button>
+        </li>
+        <li class='page-item'>
+          <button type='button' class='page-link' id='sheet:pageActiontoPage-4' title='Page 4'>4<tobago-behavior event='click' client-id='sheet:pageActiontoPage-4' execute='sheet sheet:pageActiontoPage-4' render='sheet'></tobago-behavior></button>
+        </li>
+        <li class='page-item'>
+          <button type='button' class='page-link' id='sheet:pageActiontoPage-5' title='Page 5'>5<tobago-behavior event='click' client-id='sheet:pageActiontoPage-5' execute='sheet sheet:pageActiontoPage-5' render='sheet'></tobago-behavior></button>
+        </li>
+      </ul>
+    </tobago-paginator-list>
+    <tobago-paginator-page id='sheet:_paginator_page' class='pagination'>
       <li class='disabled page-item'>
         <button type='button' class='page-link' id='sheet:pageActionfirst' title='First Page' disabled='disabled'><i class='bi-skip-start'></i></button>
       </li>
@@ -76,7 +78,7 @@
         <button type='button' class='page-link' id='sheet:pageActionprev' title='Previous Page' disabled='disabled'><i class='bi-caret-left'></i></button>
       </li>
       <li class='page-item'>
-        <span class='tobago-paging page-link' title='Click here, to change the displayed page'>Page <span>1</span><input id='sheet:pageActiontoPage' name='sheet:pageActiontoPage' type='text' value='1' maxlength='1'> of 5</span>
+        <span class='tobago-paging page-link text-nowrap' title='Click here, to change the displayed page'>Page <span>1</span><input id='sheet:pageActiontoPage' name='sheet:pageActiontoPage' type='text' value='1' maxlength='1'> of 5</span>
       </li>
       <li class='page-item'>
         <button type='button' class='page-link' id='sheet:pageActionnext' title='Next Page'><i class='bi-caret-right'></i><tobago-behavior event='click' client-id='sheet:pageActionnext' execute='sheet sheet:pageActionnext' render='sheet'></tobago-behavior></button>
@@ -84,6 +86,6 @@
       <li class='page-item'>
         <button type='button' class='page-link' id='sheet:pageActionlast' title='Last Page'><i class='bi-skip-end'></i><tobago-behavior event='click' client-id='sheet:pageActionlast' execute='sheet sheet:pageActionlast' render='sheet'></tobago-behavior></button>
       </li>
-    </ul>
-  </footer>
+    </tobago-paginator-page>
+  </tobago-paginator-panel>
 </tobago-sheet>
diff --git a/tobago-example/tobago-example-demo/src/main/java/org/apache/myfaces/tobago/example/demo/AstroData.java b/tobago-example/tobago-example-demo/src/main/java/org/apache/myfaces/tobago/example/demo/AstroData.java
index 6c93c3c20e..d38f5947d7 100644
--- a/tobago-example/tobago-example-demo/src/main/java/org/apache/myfaces/tobago/example/demo/AstroData.java
+++ b/tobago-example/tobago-example-demo/src/main/java/org/apache/myfaces/tobago/example/demo/AstroData.java
@@ -35,6 +35,7 @@ import java.util.Arrays;
 import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
+import java.util.concurrent.atomic.AtomicInteger;
 import java.util.stream.Collectors;
 import java.util.stream.Stream;
 
@@ -72,7 +73,11 @@ public class AstroData implements Serializable {
   }
 
   public Stream<SolarObject> findAll() {
-    return dataList.stream();
+    final AtomicInteger counter = new AtomicInteger(0);
+    return dataList.stream().map(object -> {
+      object.setId(counter.incrementAndGet());
+      return object;
+    });
   }
 
   public Map<String, SolarObject> findAllAsMap() {
diff --git a/tobago-example/tobago-example-demo/src/main/java/org/apache/myfaces/tobago/example/demo/SheetController.java b/tobago-example/tobago-example-demo/src/main/java/org/apache/myfaces/tobago/example/demo/SheetController.java
index b55a2758a9..c50f7cce0b 100644
--- a/tobago-example/tobago-example-demo/src/main/java/org/apache/myfaces/tobago/example/demo/SheetController.java
+++ b/tobago-example/tobago-example-demo/src/main/java/org/apache/myfaces/tobago/example/demo/SheetController.java
@@ -68,6 +68,8 @@ public class SheetController implements Serializable {
 
   private List<SolarObject> solarList;
   private List<SolarObject> hugeSolarList;
+  private List<SolarObject> smallSolarList;
+  private List<SolarObject> emptySolarList;
   private SheetState sheetState;
   private SolarObject selectedSolarObject;
   private boolean automaticLayout;
@@ -83,6 +85,13 @@ public class SheetController implements Serializable {
   private void init() {
     solarList = astroData.findAll().collect(Collectors.toList());
 
+    smallSolarList = new ArrayList<>();
+    smallSolarList.add(solarList.get(0));
+    smallSolarList.add(solarList.get(1));
+    smallSolarList.add(solarList.get(2));
+
+    emptySolarList = new ArrayList<>();
+
     int j = 1;
     hugeSolarList = new ArrayList<>();
     while (true) {
@@ -106,6 +115,14 @@ public class SheetController implements Serializable {
     return hugeSolarList;
   }
 
+  public List<SolarObject> getSmallSolarList() {
+    return smallSolarList;
+  }
+
+  public List<SolarObject> getEmptySolarList() {
+    return emptySolarList;
+  }
+
   public SheetState getSheetState() {
     return sheetState;
   }
diff --git a/tobago-example/tobago-example-demo/src/main/java/org/apache/myfaces/tobago/example/demo/SolarObject.java b/tobago-example/tobago-example-demo/src/main/java/org/apache/myfaces/tobago/example/demo/SolarObject.java
index 5e7130344b..393695e6fd 100644
--- a/tobago-example/tobago-example-demo/src/main/java/org/apache/myfaces/tobago/example/demo/SolarObject.java
+++ b/tobago-example/tobago-example-demo/src/main/java/org/apache/myfaces/tobago/example/demo/SolarObject.java
@@ -33,6 +33,8 @@ public class SolarObject implements Serializable {
 
   private static final Logger LOG = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass());
 
+  private Integer id;
+
   private String name;
 
   private String number;
@@ -88,14 +90,22 @@ public class SolarObject implements Serializable {
         : null;
   }
 
-  public String getName() {
-    return name;
-  }
-
   public void update(final AjaxBehaviorEvent event) {
     LOG.info("AjaxBehaviorEvent called. New value: '{}' event: {}", name, event);
   }
 
+  public Integer getId() {
+    return id;
+  }
+
+  public void setId(Integer id) {
+    this.id = id;
+  }
+
+  public String getName() {
+    return name;
+  }
+
   public void setName(final String name) {
     this.name = name;
   }
diff --git a/tobago-example/tobago-example-demo/src/main/webapp/content/080-sheet/15-paginator/Paginator.xhtml b/tobago-example/tobago-example-demo/src/main/webapp/content/080-sheet/15-paginator/Paginator.xhtml
new file mode 100644
index 0000000000..9eea9da5f4
--- /dev/null
+++ b/tobago-example/tobago-example-demo/src/main/webapp/content/080-sheet/15-paginator/Paginator.xhtml
@@ -0,0 +1,78 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+<!--
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+-->
+
+<ui:composition template="/main.xhtml"
+                xmlns="http://www.w3.org/1999/xhtml"
+                xmlns:tc="http://myfaces.apache.org/tobago/component"
+                xmlns:ui="http://xmlns.jcp.org/jsf/facelets"
+                xmlns:f="http://xmlns.jcp.org/jsf/core">
+
+  <tc:section label="New implementation of paginators">
+
+    Since Tobago 5.14.0 and 6.6.0 there are new Paginator tags. You can use them to define the paginator by
+    using the new attribute <code>paginator</code>. Possible values are
+    <ul>
+      <li><code>auto</code> automatically select an paginator</li>
+      <li><code>custom</code> render no paginator, the paginator will be provieded explicitly as tags</li>
+      <li><code>list</code> render a list of pages</li>
+      <li><code>row</code> render the row, it can be modified by click</li>
+      <li><code>page</code> render the page, it can be modiefied by click</li>
+      <li><code>useShowAttributes</code> the classic paginator for backward compatibility
+        (deprected, will be removed in further releases)
+      </li>
+    </ul>
+
+    <tc:badge markup="warning">Todo: Check if 2 or more of the same controls will work on same page!</tc:badge>
+
+    <tc:sheet id="s1" value="#{sheetController.solarList}" var="object" rows="5"
+              paginator="custom">
+      <!--
+      <f:facet name="before">
+        <tc:paginatorPanel>
+          <tc:paginatorRow />
+          <tc:paginatorList arrows="hide"/>
+          <tc:paginatorPage />
+        </tc:paginatorPanel>
+      </f:facet>
+      -->
+      <f:facet name="after">
+        <tc:paginatorPanel>
+          <tc:paginatorRow/>
+          <tc:paginatorList arrows="hide"/>
+          <tc:paginatorPage/>
+        </tc:paginatorPanel>
+      </f:facet>
+      <tc:column label="Name">
+        <tc:out value="#{object.name}"/>
+      </tc:column>
+      <tc:column label="Orbit">
+        <tc:out value="#{object.orbit}"/>
+      </tc:column>
+      <tc:column label="Period (Days)">
+        <tc:out value="#{object.period}"/>
+      </tc:column>
+      <tc:column label="Discoverer">
+        <tc:out value="#{object.discoverer}"/>
+      </tc:column>
+      <tc:column label="Year">
+        <tc:out value="#{object.discoverYear}"/>
+      </tc:column>
+    </tc:sheet>
+  </tc:section>
+</ui:composition>
diff --git a/tobago-example/tobago-example-demo/src/main/webapp/content/900-test/3000-sheet/40-paginator/Paginator.xhtml b/tobago-example/tobago-example-demo/src/main/webapp/content/900-test/3000-sheet/40-paginator/Paginator.xhtml
new file mode 100644
index 0000000000..1ae72af8d4
--- /dev/null
+++ b/tobago-example/tobago-example-demo/src/main/webapp/content/900-test/3000-sheet/40-paginator/Paginator.xhtml
@@ -0,0 +1,89 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+<!--
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+-->
+
+<ui:composition template="/main.xhtml"
+                xmlns="http://www.w3.org/1999/xhtml"
+                xmlns:tc="http://myfaces.apache.org/tobago/component"
+                xmlns:ui="http://xmlns.jcp.org/jsf/facelets"
+                xmlns:f="http://xmlns.jcp.org/jsf/core">
+
+  <tc:section label="List: default (5 of 88 entries)">
+    <tc:sheet id="l1" value="#{sheetController.solarList}" var="object" rows="5" paginator="list">
+      <tc:column label="Id">
+        <tc:out value="#{object.id}"/>
+      </tc:column>
+      <tc:column label="Name">
+        <tc:out value="#{object.name}"/>
+      </tc:column>
+    </tc:sheet>
+  </tc:section>
+
+  <tc:section label="List: default (5 of 3 entries)">
+    <tc:sheet id="l2" value="#{sheetController.smallSolarList}" var="object" rows="5" paginator="list">
+      <tc:column label="Id">
+        <tc:out value="#{object.id}"/>
+      </tc:column>
+      <tc:column label="Name">
+        <tc:out value="#{object.name}"/>
+      </tc:column>
+    </tc:sheet>
+  </tc:section>
+
+  <tc:section label="Page">
+    <tc:sheet id="p1" value="#{sheetController.solarList}" var="object" rows="5" paginator="page">
+      <tc:column label="Id">
+        <tc:out value="#{object.id}"/>
+      </tc:column>
+      <tc:column label="Name">
+        <tc:out value="#{object.name}"/>
+      </tc:column>
+    </tc:sheet>
+  </tc:section>
+
+  <tc:section label="Page">
+    <tc:sheet id="r1" value="#{sheetController.solarList}" var="object" rows="5" paginator="row">
+      <tc:column label="Id">
+        <tc:out value="#{object.id}"/>
+      </tc:column>
+      <tc:column label="Name">
+        <tc:out value="#{object.name}"/>
+      </tc:column>
+    </tc:sheet>
+  </tc:section>
+
+  <tc:section label="List: default (2 of 3 entries)">
+    <tc:sheet id="c1" value="#{sheetController.solarList}" var="object" rows="5" paginator="custom">
+      <f:facet name="after">
+        <f:facet name="after">
+          <tc:paginatorPanel>
+            <tc:paginatorList alwaysVisible="true"/>
+          </tc:paginatorPanel>
+        </f:facet>
+      </f:facet>
+      <tc:column label="Id">
+        <tc:out value="#{object.id}"/>
+      </tc:column>
+      <tc:column label="Name">
+        <tc:out value="#{object.name}"/>
+      </tc:column>
+    </tc:sheet>
+  </tc:section>
+
+
+</ui:composition>
diff --git a/tobago-example/tobago-example-demo/src/main/webapp/content/900-test/3000-sheet/41-paginator/Paginator_Backward_Compatibility.xhtml b/tobago-example/tobago-example-demo/src/main/webapp/content/900-test/3000-sheet/41-paginator/Paginator_Backward_Compatibility.xhtml
new file mode 100644
index 0000000000..aaa54895cb
--- /dev/null
+++ b/tobago-example/tobago-example-demo/src/main/webapp/content/900-test/3000-sheet/41-paginator/Paginator_Backward_Compatibility.xhtml
@@ -0,0 +1,217 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+<!--
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+-->
+
+<ui:composition template="/main.xhtml"
+                xmlns="http://www.w3.org/1999/xhtml"
+                xmlns:tc="http://myfaces.apache.org/tobago/component"
+                xmlns:ui="http://xmlns.jcp.org/jsf/facelets">
+
+  <tc:section label="Use show-Attributes: default">
+    <tc:sheet id="s1" value="#{sheetController.solarList}" var="object" rows="2">
+      <tc:column label="Id">
+        <tc:out value="#{object.id}"/>
+      </tc:column>
+      <tc:column label="Name">
+        <tc:out value="#{object.name}"/>
+      </tc:column>
+    </tc:sheet>
+  </tc:section>
+
+  <tc:section label="Use show-Attributes: row | space | space">
+    <tc:sheet id="s2" value="#{sheetController.solarList}" var="object" rows="2"
+              showRowRange="left" showDirectLinks="none" showPageRange="none">
+      <tc:column label="Id">
+        <tc:out value="#{object.id}"/>
+      </tc:column>
+      <tc:column label="Name">
+        <tc:out value="#{object.name}"/>
+      </tc:column>
+    </tc:sheet>
+  </tc:section>
+
+  <tc:section label="Use show-Attributes: space | row | space">
+    <tc:sheet id="s3" value="#{sheetController.solarList}" var="object" rows="2"
+              showRowRange="center" showDirectLinks="none" showPageRange="none">
+      <tc:column label="Id">
+        <tc:out value="#{object.id}"/>
+      </tc:column>
+      <tc:column label="Name">
+        <tc:out value="#{object.name}"/>
+      </tc:column>
+    </tc:sheet>
+  </tc:section>
+
+  <tc:section label="Use show-Attributes: space | space | row">
+    <tc:sheet id="s4" value="#{sheetController.solarList}" var="object" rows="2"
+              showRowRange="right" showDirectLinks="none" showPageRange="none">
+      <tc:column label="Id">
+        <tc:out value="#{object.id}"/>
+      </tc:column>
+      <tc:column label="Name">
+        <tc:out value="#{object.name}"/>
+      </tc:column>
+    </tc:sheet>
+  </tc:section>
+
+  <tc:section label="Use show-Attributes: list | space | space">
+    <tc:sheet id="s5" value="#{sheetController.solarList}" var="object" rows="2"
+              showRowRange="none" showDirectLinks="left" showPageRange="none">
+      <tc:column label="Id">
+        <tc:out value="#{object.id}"/>
+      </tc:column>
+      <tc:column label="Name">
+        <tc:out value="#{object.name}"/>
+      </tc:column>
+    </tc:sheet>
+  </tc:section>
+
+  <tc:section label="Use show-Attributes: space | list | space">
+    <tc:sheet id="s6" value="#{sheetController.solarList}" var="object" rows="2"
+              showRowRange="none" showDirectLinks="center" showPageRange="none">
+      <tc:column label="Id">
+        <tc:out value="#{object.id}"/>
+      </tc:column>
+      <tc:column label="Name">
+        <tc:out value="#{object.name}"/>
+      </tc:column>
+    </tc:sheet>
+  </tc:section>
+
+  <tc:section label="Use show-Attributes: space | space | list">
+    <tc:sheet id="s7" value="#{sheetController.solarList}" var="object" rows="2"
+              showRowRange="none" showDirectLinks="right" showPageRange="none">
+      <tc:column label="Id">
+        <tc:out value="#{object.id}"/>
+      </tc:column>
+      <tc:column label="Name">
+        <tc:out value="#{object.name}"/>
+      </tc:column>
+    </tc:sheet>
+  </tc:section>
+
+  <tc:section label="Use show-Attributes: page | space | space">
+    <tc:sheet id="s8" value="#{sheetController.solarList}" var="object" rows="2"
+              showRowRange="none" showDirectLinks="none" showPageRange="left">
+      <tc:column label="Id">
+        <tc:out value="#{object.id}"/>
+      </tc:column>
+      <tc:column label="Name">
+        <tc:out value="#{object.name}"/>
+      </tc:column>
+    </tc:sheet>
+  </tc:section>
+
+  <tc:section label="Use show-Attributes: space | page | space">
+    <tc:sheet id="s9" value="#{sheetController.solarList}" var="object" rows="2"
+              showRowRange="none" showDirectLinks="none" showPageRange="center">
+      <tc:column label="Id">
+        <tc:out value="#{object.id}"/>
+      </tc:column>
+      <tc:column label="Name">
+        <tc:out value="#{object.name}"/>
+      </tc:column>
+    </tc:sheet>
+  </tc:section>
+
+  <tc:section label="Use show-Attributes: space | space | page">
+    <tc:sheet id="s10" value="#{sheetController.solarList}" var="object" rows="2"
+              showRowRange="none" showDirectLinks="none" showPageRange="right">
+      <tc:column label="Id">
+        <tc:out value="#{object.id}"/>
+      </tc:column>
+      <tc:column label="Name">
+        <tc:out value="#{object.name}"/>
+      </tc:column>
+    </tc:sheet>
+  </tc:section>
+
+  <tc:section label="Use show-Attributes: list | page | row">
+    <tc:sheet id="s11" value="#{sheetController.solarList}" var="object" rows="2"
+              showRowRange="right" showDirectLinks="left" showPageRange="center">
+      <tc:column label="Id">
+        <tc:out value="#{object.id}"/>
+      </tc:column>
+      <tc:column label="Name">
+        <tc:out value="#{object.name}"/>
+      </tc:column>
+    </tc:sheet>
+  </tc:section>
+
+
+  <tc:section label="Use show-Attributes: showPagingAlways=false (default), 3 entries (rows=5)">
+    <tc:sheet id="s12" value="#{sheetController.smallSolarList}" var="object" rows="5">
+      <tc:column label="Id">
+        <tc:out value="#{object.id}"/>
+      </tc:column>
+      <tc:column label="Name">
+        <tc:out value="#{object.name}"/>
+      </tc:column>
+    </tc:sheet>
+  </tc:section>
+
+  <tc:section label="Use show-Attributes: showPagingAlways=true, 3 entries (rows=5)">
+    <tc:sheet id="s13" value="#{sheetController.smallSolarList}" var="object" rows="5"
+              showPagingAlways="true">
+      <tc:column label="Id">
+        <tc:out value="#{object.id}"/>
+      </tc:column>
+      <tc:column label="Name">
+        <tc:out value="#{object.name}"/>
+      </tc:column>
+    </tc:sheet>
+  </tc:section>
+
+  <tc:section label="Use show-Attributes: showDirectLinksArrows">
+    <tc:sheet id="s14" value="#{sheetController.smallSolarList}" var="object" rows="2"
+              showDirectLinksArrows="true">
+      <tc:column label="Id">
+        <tc:out value="#{object.id}"/>
+      </tc:column>
+      <tc:column label="Name">
+        <tc:out value="#{object.name}"/>
+      </tc:column>
+    </tc:sheet>
+  </tc:section>
+
+  <tc:section label="Use show-Attributes: directLinkCount=4">
+    <tc:sheet id="s15" value="#{sheetController.solarList}" var="object" rows="2"
+              showRowRange="none" showDirectLinks="center" showPageRange="none" directLinkCount="3">
+      <tc:column label="Id">
+        <tc:out value="#{object.id}"/>
+      </tc:column>
+      <tc:column label="Name">
+        <tc:out value="#{object.name}"/>
+      </tc:column>
+    </tc:sheet>
+  </tc:section>
+
+  <tc:section label="Use show-Attributes: showPageRangeArrows = false">
+    <tc:sheet id="s16" value="#{sheetController.solarList}" var="object" rows="2"
+              showRowRange="none" showDirectLinks="none" showPageRange="center"
+              showPageRangeArrows="false">
+      <tc:column label="Id">
+        <tc:out value="#{object.id}"/>
+      </tc:column>
+      <tc:column label="Name">
+        <tc:out value="#{object.name}"/>
+      </tc:column>
+    </tc:sheet>
+  </tc:section>
+
+</ui:composition>
diff --git a/tobago-theme/src/main/scss/_tobago.scss b/tobago-theme/src/main/scss/_tobago.scss
index f3030f25e8..9a7c780da4 100644
--- a/tobago-theme/src/main/scss/_tobago.scss
+++ b/tobago-theme/src/main/scss/_tobago.scss
@@ -1104,6 +1104,8 @@ tobago-overlay {
   }
 }
 
+/* page ----------------------------------------------------------- */
+
 tobago-page {
   display: block;
   padding-top: $tobago-page-padding-top;
@@ -1173,6 +1175,75 @@ tobago-page {
   }
 }
 
+/* paginator-panel ------------------------------------------------------------- */
+
+tobago-paginator-panel {
+}
+
+/* paginator-list ------------------------------------------------------------- */
+
+tobago-paginator-list {
+}
+
+/* paginator-number ------------------------------------------------------------- */
+
+tobago-paginator-page {
+}
+
+/* paginator-row ------------------------------------------------------------- */
+
+tobago-paginator-row {
+
+}
+
+tobago-paginator-list, tobago-paginator-page, tobago-paginator-row {
+  margin-top: 1rem;
+  margin-bottom: 0;
+
+  .page-link {
+    cursor: pointer;
+
+    input {
+      text-align: center;
+      display: none;
+      margin: -4px 0;
+      width: 3em;
+    }
+  }
+}
+
+/* paginator-list ------------------------------------------------------------- */
+
+tobago-paginator-list {
+}
+
+/* paginator-number ------------------------------------------------------------- */
+
+tobago-paginator-page {
+}
+
+/* paginator-row ------------------------------------------------------------- */
+
+tobago-paginator-row {
+
+}
+
+tobago-paginator-list, tobago-paginator-page, tobago-paginator-row {
+  margin-top: 1rem;
+  margin-bottom: 0;
+
+  .page-link {
+    cursor: pointer;
+
+    input {
+      text-align: center;
+      display: none;
+      margin: -4px 0;
+      width: 3em;
+    }
+  }
+}
+
 /* panel ------------------------------------------------------------- */
 
 // todo: remove
@@ -1678,6 +1749,8 @@ tobago-sheet {
     display: flex;
     flex-wrap: wrap;
 
+    // XXX TOBAGO-2243 might to be removed, because it's now directly in the new components
+
     .pagination {
       margin-top: 1rem;
       margin-bottom: 0;
diff --git a/tobago-theme/tobago-theme-charlotteville/src/main/css/tobago.css b/tobago-theme/tobago-theme-charlotteville/src/main/css/tobago.css
index 88055521a9..524b7327cd 100644
--- a/tobago-theme/tobago-theme-charlotteville/src/main/css/tobago.css
+++ b/tobago-theme/tobago-theme-charlotteville/src/main/css/tobago.css
@@ -12938,6 +12938,7 @@ tobago-overlay.modal-backdrop:not(.show) {
   display: none;
 }
 
+/* page ----------------------------------------------------------- */
 tobago-page {
   display: block;
   padding-top: 1rem;
@@ -13039,6 +13040,41 @@ tobago-page .tobago-page-toastStore .toast .tobago-auto-spacing {
   overflow-y: auto;
 }
 
+/* paginator-panel ------------------------------------------------------------- */
+/* paginator-list ------------------------------------------------------------- */
+/* paginator-number ------------------------------------------------------------- */
+/* paginator-row ------------------------------------------------------------- */
+tobago-paginator-list, tobago-paginator-page, tobago-paginator-row {
+  margin-top: 1rem;
+  margin-bottom: 0;
+}
+tobago-paginator-list .page-link, tobago-paginator-page .page-link, tobago-paginator-row .page-link {
+  cursor: pointer;
+}
+tobago-paginator-list .page-link input, tobago-paginator-page .page-link input, tobago-paginator-row .page-link input {
+  text-align: center;
+  display: none;
+  margin: -4px 0;
+  width: 3em;
+}
+
+/* paginator-list ------------------------------------------------------------- */
+/* paginator-number ------------------------------------------------------------- */
+/* paginator-row ------------------------------------------------------------- */
+tobago-paginator-list, tobago-paginator-page, tobago-paginator-row {
+  margin-top: 1rem;
+  margin-bottom: 0;
+}
+tobago-paginator-list .page-link, tobago-paginator-page .page-link, tobago-paginator-row .page-link {
+  cursor: pointer;
+}
+tobago-paginator-list .page-link input, tobago-paginator-page .page-link input, tobago-paginator-row .page-link input {
+  text-align: center;
+  display: none;
+  margin: -4px 0;
+  width: 3em;
+}
+
 /* panel ------------------------------------------------------------- */
 tobago-panel {
   display: block;
diff --git a/tobago-theme/tobago-theme-charlotteville/src/main/css/tobago.css.map b/tobago-theme/tobago-theme-charlotteville/src/main/css/tobago.css.map
index 43faf703a2..9aa728bd8f 100644
--- a/tobago-theme/tobago-theme-charlotteville/src/main/css/tobago.css.map
+++ b/tobago-theme/tobago-theme-charlotteville/src/main/css/tobago.css.map
@@ -1 +1 @@
-{"version":3,"sources":["tobago.css","../scss/tobago-theme.scss","../scss/_custom.scss","../../../../node_modules/bootstrap/scss/mixins/_banner.scss","../../../../node_modules/bootstrap/scss/_root.scss","../../../../node_modules/bootstrap/scss/vendor/_rfs.scss","../../../../node_modules/bootstrap/scss/mixins/_color-mode.scss","../../../../node_modules/bootstrap/scss/_reboot.scss","../../../../node_modules/bootstrap/scss/_variables.scss","../../../../node_modules/bootstrap/scss/mixins/_bo [...]
\ No newline at end of file
+{"version":3,"sources":["tobago.css","../scss/tobago-theme.scss","../scss/_custom.scss","../../../../node_modules/bootstrap/scss/mixins/_banner.scss","../../../../node_modules/bootstrap/scss/_root.scss","../../../../node_modules/bootstrap/scss/vendor/_rfs.scss","../../../../node_modules/bootstrap/scss/mixins/_color-mode.scss","../../../../node_modules/bootstrap/scss/_reboot.scss","../../../../node_modules/bootstrap/scss/_variables.scss","../../../../node_modules/bootstrap/scss/mixins/_bo [...]
\ No newline at end of file
diff --git a/tobago-theme/tobago-theme-charlotteville/src/main/css/tobago.min.css b/tobago-theme/tobago-theme-charlotteville/src/main/css/tobago.min.css
index f1557cc728..efd21ed645 100644
--- a/tobago-theme/tobago-theme-charlotteville/src/main/css/tobago.min.css
+++ b/tobago-theme/tobago-theme-charlotteville/src/main/css/tobago.min.css
@@ -1,2 +1,2 @@
-@charset "UTF-8";:root,[data-bs-theme=light]{--bs-blue:#0d6efd;--bs-indigo:#6610f2;--bs-purple:#6f42c1;--bs-pink:#ff00be;--bs-red:#dc3545;--bs-orange:#fd7e14;--bs-yellow:#ffc107;--bs-green:#198754;--bs-teal:#20c997;--bs-cyan:#0dcaf0;--bs-black:#000000;--bs-white:#ffffff;--bs-gray:#777777;--bs-gray-dark:#323232;--bs-gray-100:#f8f9fa;--bs-gray-200:#d0d0d0;--bs-gray-300:#dee2e6;--bs-gray-400:#a0a0a0;--bs-gray-500:#adb5bd;--bs-gray-600:#777777;--bs-gray-700:#495057;--bs-gray-800:#323232;--bs [...]
+@charset "UTF-8";:root,[data-bs-theme=light]{--bs-blue:#0d6efd;--bs-indigo:#6610f2;--bs-purple:#6f42c1;--bs-pink:#ff00be;--bs-red:#dc3545;--bs-orange:#fd7e14;--bs-yellow:#ffc107;--bs-green:#198754;--bs-teal:#20c997;--bs-cyan:#0dcaf0;--bs-black:#000000;--bs-white:#ffffff;--bs-gray:#777777;--bs-gray-dark:#323232;--bs-gray-100:#f8f9fa;--bs-gray-200:#d0d0d0;--bs-gray-300:#dee2e6;--bs-gray-400:#a0a0a0;--bs-gray-500:#adb5bd;--bs-gray-600:#777777;--bs-gray-700:#495057;--bs-gray-800:#323232;--bs [...]
 /*# sourceMappingURL=tobago.min.css.map */
\ No newline at end of file
diff --git a/tobago-theme/tobago-theme-charlotteville/src/main/css/tobago.min.css.map b/tobago-theme/tobago-theme-charlotteville/src/main/css/tobago.min.css.map
index 6fca70db98..9a5a9e2b7a 100644
--- a/tobago-theme/tobago-theme-charlotteville/src/main/css/tobago.min.css.map
+++ b/tobago-theme/tobago-theme-charlotteville/src/main/css/tobago.min.css.map
@@ -1 +1 @@
-{"version":3,"sources":["tobago-theme-charlotteville/src/main/css/tobago.css"],"names":[],"mappings":"iBAsCA,MACA,sBACE,UAAW,QACX,YAAa,QACb,YAAa,QACb,UAAW,QACX,SAAU,QACV,YAAa,QACb,YAAa,QACb,WAAY,QACZ,UAAW,QACX,UAAW,QACX,WAAY,QACZ,WAAY,QACZ,UAAW,QACX,eAAgB,QAChB,cAAe,QACf,cAAe,QACf,cAAe,QACf,cAAe,QACf,cAAe,QACf,cAAe,QACf,cAAe,QACf,cAAe,QACf,cAAe,QACf,aAAc,QACd,eAAgB,QAChB,aAAc,QACd,UAAW,QACX,aAAc,QACd,YAAa,QACb,WAAY,QACZ,UAAW,QACX,iBAAkB,EAAE,CAAE,GAAG,CAAE,IAC3B,mBAAoB,GAAG,CAAE,GAAG,CAA [...]
\ No newline at end of file
+{"version":3,"sources":["tobago-theme-charlotteville/src/main/css/tobago.css"],"names":[],"mappings":"iBAsCA,MACA,sBACE,UAAW,QACX,YAAa,QACb,YAAa,QACb,UAAW,QACX,SAAU,QACV,YAAa,QACb,YAAa,QACb,WAAY,QACZ,UAAW,QACX,UAAW,QACX,WAAY,QACZ,WAAY,QACZ,UAAW,QACX,eAAgB,QAChB,cAAe,QACf,cAAe,QACf,cAAe,QACf,cAAe,QACf,cAAe,QACf,cAAe,QACf,cAAe,QACf,cAAe,QACf,cAAe,QACf,aAAc,QACd,eAAgB,QAChB,aAAc,QACd,UAAW,QACX,aAAc,QACd,YAAa,QACb,WAAY,QACZ,UAAW,QACX,iBAAkB,EAAE,CAAE,GAAG,CAAE,IAC3B,mBAAoB,GAAG,CAAE,GAAG,CAA [...]
\ No newline at end of file
diff --git a/tobago-theme/tobago-theme-roxborough/src/main/css/tobago.css b/tobago-theme/tobago-theme-roxborough/src/main/css/tobago.css
index f7e2c833b8..8f981263d6 100644
--- a/tobago-theme/tobago-theme-roxborough/src/main/css/tobago.css
+++ b/tobago-theme/tobago-theme-roxborough/src/main/css/tobago.css
@@ -12965,6 +12965,7 @@ tobago-overlay.modal-backdrop:not(.show) {
   display: none;
 }
 
+/* page ----------------------------------------------------------- */
 tobago-page {
   display: block;
   padding-top: 1rem;
@@ -13066,6 +13067,41 @@ tobago-page .tobago-page-toastStore .toast .tobago-auto-spacing {
   overflow-y: auto;
 }
 
+/* paginator-panel ------------------------------------------------------------- */
+/* paginator-list ------------------------------------------------------------- */
+/* paginator-number ------------------------------------------------------------- */
+/* paginator-row ------------------------------------------------------------- */
+tobago-paginator-list, tobago-paginator-page, tobago-paginator-row {
+  margin-top: 1rem;
+  margin-bottom: 0;
+}
+tobago-paginator-list .page-link, tobago-paginator-page .page-link, tobago-paginator-row .page-link {
+  cursor: pointer;
+}
+tobago-paginator-list .page-link input, tobago-paginator-page .page-link input, tobago-paginator-row .page-link input {
+  text-align: center;
+  display: none;
+  margin: -4px 0;
+  width: 3em;
+}
+
+/* paginator-list ------------------------------------------------------------- */
+/* paginator-number ------------------------------------------------------------- */
+/* paginator-row ------------------------------------------------------------- */
+tobago-paginator-list, tobago-paginator-page, tobago-paginator-row {
+  margin-top: 1rem;
+  margin-bottom: 0;
+}
+tobago-paginator-list .page-link, tobago-paginator-page .page-link, tobago-paginator-row .page-link {
+  cursor: pointer;
+}
+tobago-paginator-list .page-link input, tobago-paginator-page .page-link input, tobago-paginator-row .page-link input {
+  text-align: center;
+  display: none;
+  margin: -4px 0;
+  width: 3em;
+}
+
 /* panel ------------------------------------------------------------- */
 tobago-panel {
   display: block;
diff --git a/tobago-theme/tobago-theme-roxborough/src/main/css/tobago.css.map b/tobago-theme/tobago-theme-roxborough/src/main/css/tobago.css.map
index c45cd3586f..b37b499aad 100644
--- a/tobago-theme/tobago-theme-roxborough/src/main/css/tobago.css.map
+++ b/tobago-theme/tobago-theme-roxborough/src/main/css/tobago.css.map
@@ -1 +1 @@
-{"version":3,"sources":["tobago.css","../scss/tobago-theme.scss","../scss/_custom.scss","../../../../node_modules/bootstrap/scss/mixins/_banner.scss","../../../../node_modules/bootstrap/scss/_root.scss","../../../../node_modules/bootstrap/scss/vendor/_rfs.scss","../../../../node_modules/bootstrap/scss/mixins/_color-mode.scss","../../../../node_modules/bootstrap/scss/_reboot.scss","../../../../node_modules/bootstrap/scss/_variables.scss","../../../../node_modules/bootstrap/scss/mixins/_bo [...]
\ No newline at end of file
+{"version":3,"sources":["tobago.css","../scss/tobago-theme.scss","../scss/_custom.scss","../../../../node_modules/bootstrap/scss/mixins/_banner.scss","../../../../node_modules/bootstrap/scss/_root.scss","../../../../node_modules/bootstrap/scss/vendor/_rfs.scss","../../../../node_modules/bootstrap/scss/mixins/_color-mode.scss","../../../../node_modules/bootstrap/scss/_reboot.scss","../../../../node_modules/bootstrap/scss/_variables.scss","../../../../node_modules/bootstrap/scss/mixins/_bo [...]
\ No newline at end of file
diff --git a/tobago-theme/tobago-theme-roxborough/src/main/css/tobago.min.css b/tobago-theme/tobago-theme-roxborough/src/main/css/tobago.min.css
index bf5cf119ee..4f0f515e2b 100644
--- a/tobago-theme/tobago-theme-roxborough/src/main/css/tobago.min.css
+++ b/tobago-theme/tobago-theme-roxborough/src/main/css/tobago.min.css
@@ -1,2 +1,2 @@
-@charset "UTF-8";@font-face{font-family:Amaranth;font-style:normal;font-weight:400;src:url("../fonts/Amaranth-Regular.otf") format("opentype")}@font-face{font-family:Amaranth;font-style:normal;font-weight:700;src:url("../fonts/Amaranth-Bold.otf") format("opentype")}@font-face{font-family:Amaranth;font-style:italic;src:url("../fonts/Amaranth-Italic.otf") format("opentype")}@font-face{font-family:Amaranth;font-style:italic;font-weight:700;src:url("../fonts/Amaranth-BoldItalic.otf") format( [...]
+@charset "UTF-8";@font-face{font-family:Amaranth;font-style:normal;font-weight:400;src:url("../fonts/Amaranth-Regular.otf") format("opentype")}@font-face{font-family:Amaranth;font-style:normal;font-weight:700;src:url("../fonts/Amaranth-Bold.otf") format("opentype")}@font-face{font-family:Amaranth;font-style:italic;src:url("../fonts/Amaranth-Italic.otf") format("opentype")}@font-face{font-family:Amaranth;font-style:italic;font-weight:700;src:url("../fonts/Amaranth-BoldItalic.otf") format( [...]
 /*# sourceMappingURL=tobago.min.css.map */
\ No newline at end of file
diff --git a/tobago-theme/tobago-theme-roxborough/src/main/css/tobago.min.css.map b/tobago-theme/tobago-theme-roxborough/src/main/css/tobago.min.css.map
index 4e4ed910c8..93f070f06c 100644
--- a/tobago-theme/tobago-theme-roxborough/src/main/css/tobago.min.css.map
+++ b/tobago-theme/tobago-theme-roxborough/src/main/css/tobago.min.css.map
@@ -1 +1 @@
-{"version":3,"sources":["tobago-theme-roxborough/src/main/css/tobago.css"],"names":[],"mappings":"iBAiCA,WACE,YAAa,SACb,WAAY,OACZ,YAAa,IACb,IAAK,qCAAqC,mBAE5C,WACE,YAAa,SACb,WAAY,OACZ,YAAa,IACb,IAAK,kCAAkC,mBAEzC,WACE,YAAa,SACb,WAAY,OACZ,IAAK,oCAAoC,mBAE3C,WACE,YAAa,SACb,WAAY,OACZ,YAAa,IACb,IAAK,wCAAwC,mBAE/C,mBACE,YAAa,QAAQ,CAAE,KAAK,CAAE,MAQhC,MACA,sBACE,UAAW,QACX,YAAa,QACb,YAAa,QACb,UAAW,QACX,SAAU,QACV,YAAa,QACb,YAAa,QACb,WAAY,QACZ,UAAW,QACX,UAAW,QACX,WAAY,QACZ,WAAY,KACZ,UAAW,QACX,eAA [...]
\ No newline at end of file
+{"version":3,"sources":["tobago-theme-roxborough/src/main/css/tobago.css"],"names":[],"mappings":"iBAiCA,WACE,YAAa,SACb,WAAY,OACZ,YAAa,IACb,IAAK,qCAAqC,mBAE5C,WACE,YAAa,SACb,WAAY,OACZ,YAAa,IACb,IAAK,kCAAkC,mBAEzC,WACE,YAAa,SACb,WAAY,OACZ,IAAK,oCAAoC,mBAE3C,WACE,YAAa,SACb,WAAY,OACZ,YAAa,IACb,IAAK,wCAAwC,mBAE/C,mBACE,YAAa,QAAQ,CAAE,KAAK,CAAE,MAQhC,MACA,sBACE,UAAW,QACX,YAAa,QACb,YAAa,QACb,UAAW,QACX,SAAU,QACV,YAAa,QACb,YAAa,QACb,WAAY,QACZ,UAAW,QACX,UAAW,QACX,WAAY,QACZ,WAAY,KACZ,UAAW,QACX,eAA [...]
\ No newline at end of file
diff --git a/tobago-theme/tobago-theme-scarborough/src/main/css/tobago.css b/tobago-theme/tobago-theme-scarborough/src/main/css/tobago.css
index 677f86b98d..a9d40406db 100644
--- a/tobago-theme/tobago-theme-scarborough/src/main/css/tobago.css
+++ b/tobago-theme/tobago-theme-scarborough/src/main/css/tobago.css
@@ -12959,6 +12959,7 @@ tobago-overlay.modal-backdrop:not(.show) {
   display: none;
 }
 
+/* page ----------------------------------------------------------- */
 tobago-page {
   display: block;
   padding-top: 1rem;
@@ -13061,6 +13062,41 @@ tobago-page .tobago-page-toastStore .toast .tobago-auto-spacing {
   overflow-y: auto;
 }
 
+/* paginator-panel ------------------------------------------------------------- */
+/* paginator-list ------------------------------------------------------------- */
+/* paginator-number ------------------------------------------------------------- */
+/* paginator-row ------------------------------------------------------------- */
+tobago-paginator-list, tobago-paginator-page, tobago-paginator-row {
+  margin-top: 1rem;
+  margin-bottom: 0;
+}
+tobago-paginator-list .page-link, tobago-paginator-page .page-link, tobago-paginator-row .page-link {
+  cursor: pointer;
+}
+tobago-paginator-list .page-link input, tobago-paginator-page .page-link input, tobago-paginator-row .page-link input {
+  text-align: center;
+  display: none;
+  margin: -4px 0;
+  width: 3em;
+}
+
+/* paginator-list ------------------------------------------------------------- */
+/* paginator-number ------------------------------------------------------------- */
+/* paginator-row ------------------------------------------------------------- */
+tobago-paginator-list, tobago-paginator-page, tobago-paginator-row {
+  margin-top: 1rem;
+  margin-bottom: 0;
+}
+tobago-paginator-list .page-link, tobago-paginator-page .page-link, tobago-paginator-row .page-link {
+  cursor: pointer;
+}
+tobago-paginator-list .page-link input, tobago-paginator-page .page-link input, tobago-paginator-row .page-link input {
+  text-align: center;
+  display: none;
+  margin: -4px 0;
+  width: 3em;
+}
+
 /* panel ------------------------------------------------------------- */
 tobago-panel {
   display: block;
diff --git a/tobago-theme/tobago-theme-scarborough/src/main/css/tobago.css.map b/tobago-theme/tobago-theme-scarborough/src/main/css/tobago.css.map
index 6e26a042e5..d9a443df13 100644
--- a/tobago-theme/tobago-theme-scarborough/src/main/css/tobago.css.map
+++ b/tobago-theme/tobago-theme-scarborough/src/main/css/tobago.css.map
@@ -1 +1 @@
-{"version":3,"sources":["tobago.css","../scss/tobago-theme.scss","../scss/_custom.scss","../../../../node_modules/bootstrap/scss/mixins/_banner.scss","../../../../node_modules/bootstrap/scss/_root.scss","../../../../node_modules/bootstrap/scss/vendor/_rfs.scss","../../../../node_modules/bootstrap/scss/mixins/_color-mode.scss","../../../../node_modules/bootstrap/scss/_reboot.scss","../../../../node_modules/bootstrap/scss/_variables.scss","../../../../node_modules/bootstrap/scss/mixins/_bo [...]
\ No newline at end of file
+{"version":3,"sources":["tobago.css","../scss/tobago-theme.scss","../scss/_custom.scss","../../../../node_modules/bootstrap/scss/mixins/_banner.scss","../../../../node_modules/bootstrap/scss/_root.scss","../../../../node_modules/bootstrap/scss/vendor/_rfs.scss","../../../../node_modules/bootstrap/scss/mixins/_color-mode.scss","../../../../node_modules/bootstrap/scss/_reboot.scss","../../../../node_modules/bootstrap/scss/_variables.scss","../../../../node_modules/bootstrap/scss/mixins/_bo [...]
\ No newline at end of file
diff --git a/tobago-theme/tobago-theme-scarborough/src/main/css/tobago.min.css b/tobago-theme/tobago-theme-scarborough/src/main/css/tobago.min.css
index 969b2c5203..52976a8807 100644
--- a/tobago-theme/tobago-theme-scarborough/src/main/css/tobago.min.css
+++ b/tobago-theme/tobago-theme-scarborough/src/main/css/tobago.min.css
@@ -1,2 +1,2 @@
-@charset "UTF-8";:root,[data-bs-theme=light]{--bs-blue:#0d6efd;--bs-indigo:#6610f2;--bs-purple:#6f42c1;--bs-pink:#d63384;--bs-red:#dc3545;--bs-orange:#fd7e14;--bs-yellow:#ffc107;--bs-green:#198754;--bs-teal:#20c997;--bs-cyan:#0dcaf0;--bs-black:#000;--bs-white:#fff;--bs-gray:#6c757d;--bs-gray-dark:#343a40;--bs-gray-100:#f8f9fa;--bs-gray-200:#e9ecef;--bs-gray-300:#dee2e6;--bs-gray-400:#ced4da;--bs-gray-500:#adb5bd;--bs-gray-600:#6c757d;--bs-gray-700:#495057;--bs-gray-800:#343a40;--bs-gray- [...]
+@charset "UTF-8";:root,[data-bs-theme=light]{--bs-blue:#0d6efd;--bs-indigo:#6610f2;--bs-purple:#6f42c1;--bs-pink:#d63384;--bs-red:#dc3545;--bs-orange:#fd7e14;--bs-yellow:#ffc107;--bs-green:#198754;--bs-teal:#20c997;--bs-cyan:#0dcaf0;--bs-black:#000;--bs-white:#fff;--bs-gray:#6c757d;--bs-gray-dark:#343a40;--bs-gray-100:#f8f9fa;--bs-gray-200:#e9ecef;--bs-gray-300:#dee2e6;--bs-gray-400:#ced4da;--bs-gray-500:#adb5bd;--bs-gray-600:#6c757d;--bs-gray-700:#495057;--bs-gray-800:#343a40;--bs-gray- [...]
 /*# sourceMappingURL=tobago.min.css.map */
\ No newline at end of file
diff --git a/tobago-theme/tobago-theme-scarborough/src/main/css/tobago.min.css.map b/tobago-theme/tobago-theme-scarborough/src/main/css/tobago.min.css.map
index e9cf069f74..f724626589 100644
--- a/tobago-theme/tobago-theme-scarborough/src/main/css/tobago.min.css.map
+++ b/tobago-theme/tobago-theme-scarborough/src/main/css/tobago.min.css.map
@@ -1 +1 @@
-{"version":3,"sources":["tobago-theme-scarborough/src/main/css/tobago.css"],"names":[],"mappings":"iBAwCA,MACA,sBACE,UAAW,QACX,YAAa,QACb,YAAa,QACb,UAAW,QACX,SAAU,QACV,YAAa,QACb,YAAa,QACb,WAAY,QACZ,UAAW,QACX,UAAW,QACX,WAAY,KACZ,WAAY,KACZ,UAAW,QACX,eAAgB,QAChB,cAAe,QACf,cAAe,QACf,cAAe,QACf,cAAe,QACf,cAAe,QACf,cAAe,QACf,cAAe,QACf,cAAe,QACf,cAAe,QACf,aAAc,QACd,eAAgB,QAChB,aAAc,QACd,UAAW,QACX,aAAc,QACd,YAAa,QACb,WAAY,QACZ,UAAW,QACX,iBAAkB,EAAE,CAAE,GAAG,CAAE,IAC3B,mBAAoB,GAAG,CAAE,GAAG,CAAE,I [...]
\ No newline at end of file
+{"version":3,"sources":["tobago-theme-scarborough/src/main/css/tobago.css"],"names":[],"mappings":"iBAwCA,MACA,sBACE,UAAW,QACX,YAAa,QACb,YAAa,QACb,UAAW,QACX,SAAU,QACV,YAAa,QACb,YAAa,QACb,WAAY,QACZ,UAAW,QACX,UAAW,QACX,WAAY,KACZ,WAAY,KACZ,UAAW,QACX,eAAgB,QAChB,cAAe,QACf,cAAe,QACf,cAAe,QACf,cAAe,QACf,cAAe,QACf,cAAe,QACf,cAAe,QACf,cAAe,QACf,cAAe,QACf,aAAc,QACd,eAAgB,QAChB,aAAc,QACd,UAAW,QACX,aAAc,QACd,YAAa,QACb,WAAY,QACZ,UAAW,QACX,iBAAkB,EAAE,CAAE,GAAG,CAAE,IAC3B,mBAAoB,GAAG,CAAE,GAAG,CAAE,I [...]
\ No newline at end of file
diff --git a/tobago-theme/tobago-theme-speyside/src/main/css/tobago.css b/tobago-theme/tobago-theme-speyside/src/main/css/tobago.css
index 3293f2faee..dbf7f90aa7 100644
--- a/tobago-theme/tobago-theme-speyside/src/main/css/tobago.css
+++ b/tobago-theme/tobago-theme-speyside/src/main/css/tobago.css
@@ -12656,6 +12656,7 @@ tobago-overlay.modal-backdrop:not(.show) {
   display: none;
 }
 
+/* page ----------------------------------------------------------- */
 tobago-page {
   display: block;
   padding-top: 1rem;
@@ -12757,6 +12758,41 @@ tobago-page .tobago-page-toastStore .toast .tobago-auto-spacing {
   overflow-y: auto;
 }
 
+/* paginator-panel ------------------------------------------------------------- */
+/* paginator-list ------------------------------------------------------------- */
+/* paginator-number ------------------------------------------------------------- */
+/* paginator-row ------------------------------------------------------------- */
+tobago-paginator-list, tobago-paginator-page, tobago-paginator-row {
+  margin-top: 1rem;
+  margin-bottom: 0;
+}
+tobago-paginator-list .page-link, tobago-paginator-page .page-link, tobago-paginator-row .page-link {
+  cursor: pointer;
+}
+tobago-paginator-list .page-link input, tobago-paginator-page .page-link input, tobago-paginator-row .page-link input {
+  text-align: center;
+  display: none;
+  margin: -4px 0;
+  width: 3em;
+}
+
+/* paginator-list ------------------------------------------------------------- */
+/* paginator-number ------------------------------------------------------------- */
+/* paginator-row ------------------------------------------------------------- */
+tobago-paginator-list, tobago-paginator-page, tobago-paginator-row {
+  margin-top: 1rem;
+  margin-bottom: 0;
+}
+tobago-paginator-list .page-link, tobago-paginator-page .page-link, tobago-paginator-row .page-link {
+  cursor: pointer;
+}
+tobago-paginator-list .page-link input, tobago-paginator-page .page-link input, tobago-paginator-row .page-link input {
+  text-align: center;
+  display: none;
+  margin: -4px 0;
+  width: 3em;
+}
+
 /* panel ------------------------------------------------------------- */
 tobago-panel {
   display: block;
diff --git a/tobago-theme/tobago-theme-speyside/src/main/css/tobago.css.map b/tobago-theme/tobago-theme-speyside/src/main/css/tobago.css.map
index 8a01a1b339..b34dc46645 100644
--- a/tobago-theme/tobago-theme-speyside/src/main/css/tobago.css.map
+++ b/tobago-theme/tobago-theme-speyside/src/main/css/tobago.css.map
@@ -1 +1 @@
-{"version":3,"sources":["tobago.css","../scss/tobago-theme.scss","../scss/_custom.scss","../../../../node_modules/bootstrap/scss/mixins/_banner.scss","../../../../node_modules/bootstrap/scss/_root.scss","../../../../node_modules/bootstrap/scss/vendor/_rfs.scss","../../../../node_modules/bootstrap/scss/mixins/_color-mode.scss","../../../../node_modules/bootstrap/scss/_reboot.scss","../../../../node_modules/bootstrap/scss/_variables.scss","../../../../node_modules/bootstrap/scss/_type.scss [...]
\ No newline at end of file
+{"version":3,"sources":["tobago.css","../scss/tobago-theme.scss","../scss/_custom.scss","../../../../node_modules/bootstrap/scss/mixins/_banner.scss","../../../../node_modules/bootstrap/scss/_root.scss","../../../../node_modules/bootstrap/scss/vendor/_rfs.scss","../../../../node_modules/bootstrap/scss/mixins/_color-mode.scss","../../../../node_modules/bootstrap/scss/_reboot.scss","../../../../node_modules/bootstrap/scss/_variables.scss","../../../../node_modules/bootstrap/scss/_type.scss [...]
\ No newline at end of file
diff --git a/tobago-theme/tobago-theme-speyside/src/main/css/tobago.min.css b/tobago-theme/tobago-theme-speyside/src/main/css/tobago.min.css
index d314aae30b..f188099971 100644
--- a/tobago-theme/tobago-theme-speyside/src/main/css/tobago.min.css
+++ b/tobago-theme/tobago-theme-speyside/src/main/css/tobago.min.css
@@ -1,2 +1,2 @@
-@charset "UTF-8";:root,[data-bs-theme=light]{--bs-blue:#0d6efd;--bs-indigo:#6610f2;--bs-purple:rgb(58, 37, 100);--bs-pink:#d63384;--bs-red:rgb(211, 0, 64);--bs-orange:#d90;--bs-yellow:#ffc107;--bs-green:rgb(29, 163, 50);--bs-teal:#20c997;--bs-cyan:#0dcaf0;--bs-black:#000;--bs-white:#fff;--bs-gray:rgb(120, 140, 148);--bs-gray-dark:#323232;--bs-gray-100:#f7f7f7;--bs-gray-200:#e3e4e5;--bs-gray-300:#d7d7d7;--bs-gray-400:#ced4da;--bs-gray-500:#acacac;--bs-gray-600:rgb(120, 140, 148);--bs-gray [...]
+@charset "UTF-8";:root,[data-bs-theme=light]{--bs-blue:#0d6efd;--bs-indigo:#6610f2;--bs-purple:rgb(58, 37, 100);--bs-pink:#d63384;--bs-red:rgb(211, 0, 64);--bs-orange:#d90;--bs-yellow:#ffc107;--bs-green:rgb(29, 163, 50);--bs-teal:#20c997;--bs-cyan:#0dcaf0;--bs-black:#000;--bs-white:#fff;--bs-gray:rgb(120, 140, 148);--bs-gray-dark:#323232;--bs-gray-100:#f7f7f7;--bs-gray-200:#e3e4e5;--bs-gray-300:#d7d7d7;--bs-gray-400:#ced4da;--bs-gray-500:#acacac;--bs-gray-600:rgb(120, 140, 148);--bs-gray [...]
 /*# sourceMappingURL=tobago.min.css.map */
\ No newline at end of file
diff --git a/tobago-theme/tobago-theme-speyside/src/main/css/tobago.min.css.map b/tobago-theme/tobago-theme-speyside/src/main/css/tobago.min.css.map
index e85d395d97..5f6639ec22 100644
--- a/tobago-theme/tobago-theme-speyside/src/main/css/tobago.min.css.map
+++ b/tobago-theme/tobago-theme-speyside/src/main/css/tobago.min.css.map
@@ -1 +1 @@
-{"version":3,"sources":["tobago-theme-speyside/src/main/css/tobago.css"],"names":[],"mappings":"iBAsCA,MACA,sBACE,UAAW,QACX,YAAa,QACb,YAAa,iBACb,UAAW,QACX,SAAU,gBACV,YAAa,KACb,YAAa,QACb,WAAY,iBACZ,UAAW,QACX,UAAW,QACX,WAAY,KACZ,WAAY,KACZ,UAAW,mBACX,eAAgB,QAChB,cAAe,QACf,cAAe,QACf,cAAe,QACf,cAAe,QACf,cAAe,QACf,cAAe,mBACf,cAAe,QACf,cAAe,QACf,cAAe,QACf,aAAc,gBACd,eAAgB,QAChB,aAAc,iBACd,UAAW,QACX,aAAc,QACd,YAAa,gBACb,WAAY,QACZ,UAAW,QACX,iBAAkB,EAAE,CAAE,EAAE,CAAE,GAC1B,mBAAoB,GAAG,CAAE,GAAG,C [...]
\ No newline at end of file
+{"version":3,"sources":["tobago-theme-speyside/src/main/css/tobago.css"],"names":[],"mappings":"iBAsCA,MACA,sBACE,UAAW,QACX,YAAa,QACb,YAAa,iBACb,UAAW,QACX,SAAU,gBACV,YAAa,KACb,YAAa,QACb,WAAY,iBACZ,UAAW,QACX,UAAW,QACX,WAAY,KACZ,WAAY,KACZ,UAAW,mBACX,eAAgB,QAChB,cAAe,QACf,cAAe,QACf,cAAe,QACf,cAAe,QACf,cAAe,QACf,cAAe,mBACf,cAAe,QACf,cAAe,QACf,cAAe,QACf,aAAc,gBACd,eAAgB,QAChB,aAAc,iBACd,UAAW,QACX,aAAc,QACd,YAAa,gBACb,WAAY,QACZ,UAAW,QACX,iBAAkB,EAAE,CAAE,EAAE,CAAE,GAC1B,mBAAoB,GAAG,CAAE,GAAG,C [...]
\ No newline at end of file
diff --git a/tobago-theme/tobago-theme-standard/src/main/css/tobago.css b/tobago-theme/tobago-theme-standard/src/main/css/tobago.css
index 3085e598c2..ea3f71de89 100644
--- a/tobago-theme/tobago-theme-standard/src/main/css/tobago.css
+++ b/tobago-theme/tobago-theme-standard/src/main/css/tobago.css
@@ -12922,6 +12922,7 @@ tobago-overlay.modal-backdrop:not(.show) {
   display: none;
 }
 
+/* page ----------------------------------------------------------- */
 tobago-page {
   display: block;
   padding-top: 1rem;
@@ -13023,6 +13024,41 @@ tobago-page .tobago-page-toastStore .toast .tobago-auto-spacing {
   overflow-y: auto;
 }
 
+/* paginator-panel ------------------------------------------------------------- */
+/* paginator-list ------------------------------------------------------------- */
+/* paginator-number ------------------------------------------------------------- */
+/* paginator-row ------------------------------------------------------------- */
+tobago-paginator-list, tobago-paginator-page, tobago-paginator-row {
+  margin-top: 1rem;
+  margin-bottom: 0;
+}
+tobago-paginator-list .page-link, tobago-paginator-page .page-link, tobago-paginator-row .page-link {
+  cursor: pointer;
+}
+tobago-paginator-list .page-link input, tobago-paginator-page .page-link input, tobago-paginator-row .page-link input {
+  text-align: center;
+  display: none;
+  margin: -4px 0;
+  width: 3em;
+}
+
+/* paginator-list ------------------------------------------------------------- */
+/* paginator-number ------------------------------------------------------------- */
+/* paginator-row ------------------------------------------------------------- */
+tobago-paginator-list, tobago-paginator-page, tobago-paginator-row {
+  margin-top: 1rem;
+  margin-bottom: 0;
+}
+tobago-paginator-list .page-link, tobago-paginator-page .page-link, tobago-paginator-row .page-link {
+  cursor: pointer;
+}
+tobago-paginator-list .page-link input, tobago-paginator-page .page-link input, tobago-paginator-row .page-link input {
+  text-align: center;
+  display: none;
+  margin: -4px 0;
+  width: 3em;
+}
+
 /* panel ------------------------------------------------------------- */
 tobago-panel {
   display: block;
diff --git a/tobago-theme/tobago-theme-standard/src/main/css/tobago.css.map b/tobago-theme/tobago-theme-standard/src/main/css/tobago.css.map
index ebe6e77f57..75d6249d95 100644
--- a/tobago-theme/tobago-theme-standard/src/main/css/tobago.css.map
+++ b/tobago-theme/tobago-theme-standard/src/main/css/tobago.css.map
@@ -1 +1 @@
-{"version":3,"sources":["tobago.css","../scss/tobago-theme.scss","../../../../node_modules/bootstrap/scss/mixins/_banner.scss","../../../../node_modules/bootstrap/scss/_root.scss","../../../../node_modules/bootstrap/scss/vendor/_rfs.scss","../../../../node_modules/bootstrap/scss/mixins/_color-mode.scss","../../../../node_modules/bootstrap/scss/_reboot.scss","../../../../node_modules/bootstrap/scss/_variables.scss","../../../../node_modules/bootstrap/scss/mixins/_border-radius.scss","../. [...]
\ No newline at end of file
+{"version":3,"sources":["tobago.css","../scss/tobago-theme.scss","../../../../node_modules/bootstrap/scss/mixins/_banner.scss","../../../../node_modules/bootstrap/scss/_root.scss","../../../../node_modules/bootstrap/scss/vendor/_rfs.scss","../../../../node_modules/bootstrap/scss/mixins/_color-mode.scss","../../../../node_modules/bootstrap/scss/_reboot.scss","../../../../node_modules/bootstrap/scss/_variables.scss","../../../../node_modules/bootstrap/scss/mixins/_border-radius.scss","../. [...]
\ No newline at end of file
diff --git a/tobago-theme/tobago-theme-standard/src/main/css/tobago.min.css b/tobago-theme/tobago-theme-standard/src/main/css/tobago.min.css
index 1cf224b2ef..2700ec5171 100644
--- a/tobago-theme/tobago-theme-standard/src/main/css/tobago.min.css
+++ b/tobago-theme/tobago-theme-standard/src/main/css/tobago.min.css
@@ -1,2 +1,2 @@
-@charset "UTF-8";:root,[data-bs-theme=light]{--bs-blue:#0d6efd;--bs-indigo:#6610f2;--bs-purple:#6f42c1;--bs-pink:#d63384;--bs-red:#dc3545;--bs-orange:#fd7e14;--bs-yellow:#ffc107;--bs-green:#198754;--bs-teal:#20c997;--bs-cyan:#0dcaf0;--bs-black:#000;--bs-white:#fff;--bs-gray:#6c757d;--bs-gray-dark:#343a40;--bs-gray-100:#f8f9fa;--bs-gray-200:#e9ecef;--bs-gray-300:#dee2e6;--bs-gray-400:#ced4da;--bs-gray-500:#adb5bd;--bs-gray-600:#6c757d;--bs-gray-700:#495057;--bs-gray-800:#343a40;--bs-gray- [...]
+@charset "UTF-8";:root,[data-bs-theme=light]{--bs-blue:#0d6efd;--bs-indigo:#6610f2;--bs-purple:#6f42c1;--bs-pink:#d63384;--bs-red:#dc3545;--bs-orange:#fd7e14;--bs-yellow:#ffc107;--bs-green:#198754;--bs-teal:#20c997;--bs-cyan:#0dcaf0;--bs-black:#000;--bs-white:#fff;--bs-gray:#6c757d;--bs-gray-dark:#343a40;--bs-gray-100:#f8f9fa;--bs-gray-200:#e9ecef;--bs-gray-300:#dee2e6;--bs-gray-400:#ced4da;--bs-gray-500:#adb5bd;--bs-gray-600:#6c757d;--bs-gray-700:#495057;--bs-gray-800:#343a40;--bs-gray- [...]
 /*# sourceMappingURL=tobago.min.css.map */
\ No newline at end of file
diff --git a/tobago-theme/tobago-theme-standard/src/main/css/tobago.min.css.map b/tobago-theme/tobago-theme-standard/src/main/css/tobago.min.css.map
index 126edf5143..cbfa879c62 100644
--- a/tobago-theme/tobago-theme-standard/src/main/css/tobago.min.css.map
+++ b/tobago-theme/tobago-theme-standard/src/main/css/tobago.min.css.map
@@ -1 +1 @@
-{"version":3,"sources":["tobago-theme-standard/src/main/css/tobago.css"],"names":[],"mappings":"iBAsBA,MACA,sBACE,UAAW,QACX,YAAa,QACb,YAAa,QACb,UAAW,QACX,SAAU,QACV,YAAa,QACb,YAAa,QACb,WAAY,QACZ,UAAW,QACX,UAAW,QACX,WAAY,KACZ,WAAY,KACZ,UAAW,QACX,eAAgB,QAChB,cAAe,QACf,cAAe,QACf,cAAe,QACf,cAAe,QACf,cAAe,QACf,cAAe,QACf,cAAe,QACf,cAAe,QACf,cAAe,QACf,aAAc,QACd,eAAgB,QAChB,aAAc,QACd,UAAW,QACX,aAAc,QACd,YAAa,QACb,WAAY,QACZ,UAAW,QACX,iBAAkB,EAAE,CAAE,GAAG,CAAE,IAC3B,mBAAoB,GAAG,CAAE,GAAG,CAAE,IAC9 [...]
\ No newline at end of file
+{"version":3,"sources":["tobago-theme-standard/src/main/css/tobago.css"],"names":[],"mappings":"iBAsBA,MACA,sBACE,UAAW,QACX,YAAa,QACb,YAAa,QACb,UAAW,QACX,SAAU,QACV,YAAa,QACb,YAAa,QACb,WAAY,QACZ,UAAW,QACX,UAAW,QACX,WAAY,KACZ,WAAY,KACZ,UAAW,QACX,eAAgB,QAChB,cAAe,QACf,cAAe,QACf,cAAe,QACf,cAAe,QACf,cAAe,QACf,cAAe,QACf,cAAe,QACf,cAAe,QACf,cAAe,QACf,aAAc,QACd,eAAgB,QAChB,aAAc,QACd,UAAW,QACX,aAAc,QACd,YAAa,QACb,WAAY,QACZ,UAAW,QACX,iBAAkB,EAAE,CAAE,GAAG,CAAE,IAC3B,mBAAoB,GAAG,CAAE,GAAG,CAAE,IAC9 [...]
\ No newline at end of file
diff --git a/tobago-theme/tobago-theme-standard/src/main/js/tobago.js b/tobago-theme/tobago-theme-standard/src/main/js/tobago.js
index 18690b2acb..c4bad2afb5 100644
--- a/tobago-theme/tobago-theme-standard/src/main/js/tobago.js
+++ b/tobago-theme/tobago-theme-standard/src/main/js/tobago.js
@@ -1,19 +1,19 @@
-!function(e){"function"==typeof define&&define.amd?define(e):e()}((function(){"use strict";var e,t;!function(e){e.ACTIVE="active",e.AUTOCOMPLETE="autocomplete",e.AUTOCOMPLETE_INPUT="autocomplete-input",e.BOTTOM_0="bottom-0",e.COLLAPSE="collapse",e.COLLAPSING="collapsing",e.D_NONE="d-none",e.DISABLED="disabled",e.DROPDOWN="dropdown",e.DROPDOWN_ITEM="dropdown-item",e.DROPDOWN_MENU="dropdown-menu",e.DROPDOWN_MENU_END="dropdown-menu-end",e.END_0="end-0",e.FADE="fade",e.FIXED_BOTTOM="fixed-bo [...]
+!function(e){"function"==typeof define&&define.amd?define(e):e()}((function(){"use strict";var e,t;!function(e){e.ACTIVE="active",e.AUTOCOMPLETE="autocomplete",e.AUTOCOMPLETE_INPUT="autocomplete-input",e.BOTTOM_0="bottom-0",e.COLLAPSE="collapse",e.COLLAPSING="collapsing",e.D_NONE="d-none",e.DISABLED="disabled",e.DROPDOWN="dropdown",e.DROPDOWN_ITEM="dropdown-item",e.DROPDOWN_MENU="dropdown-menu",e.DROPDOWN_MENU_END="dropdown-menu-end",e.END_0="end-0",e.FADE="fade",e.FIXED_BOTTOM="fixed-bo [...]
 /*!
       * Bootstrap v5.3.3 (https://getbootstrap.com/)
       * Copyright 2011-2024 The Bootstrap Authors (https://github.com/twbs/bootstrap/graphs/contributors)
       * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)
       */
-const pt=new Map,gt={set(e,t,s){pt.has(e)||pt.set(e,new Map);const i=pt.get(e);i.has(t)||0===i.size?i.set(t,s):console.error(`Bootstrap doesn't allow more than one instance per element. Bound instance: ${Array.from(i.keys())[0]}.`)},get:(e,t)=>pt.has(e)&&pt.get(e).get(t)||null,remove(e,t){if(!pt.has(e))return;const s=pt.get(e);s.delete(t),0===s.size&&pt.delete(e)}},ft="transitionend",mt=e=>(e&&window.CSS&&window.CSS.escape&&(e=e.replace(/#([^\s"#']+)/g,((e,t)=>`#${CSS.escape(t)}`))),e),b [...]
+const pt=new Map,gt={set(e,t,s){pt.has(e)||pt.set(e,new Map);const i=pt.get(e);i.has(t)||0===i.size?i.set(t,s):console.error(`Bootstrap doesn't allow more than one instance per element. Bound instance: ${Array.from(i.keys())[0]}.`)},get:(e,t)=>pt.has(e)&&pt.get(e).get(t)||null,remove(e,t){if(!pt.has(e))return;const s=pt.get(e);s.delete(t),0===s.size&&pt.delete(e)}},ft="transitionend",mt=e=>(e&&window.CSS&&window.CSS.escape&&(e=e.replace(/#([^\s"#']+)/g,((e,t)=>`#${CSS.escape(t)}`))),e),b [...]
 /**
      * @license
      * Copyright 2017 Google LLC
      * SPDX-License-Identifier: BSD-3-Clause
-     */const ar=globalThis,lr=ar.trustedTypes,cr=lr?lr.createPolicy("lit-html",{createHTML:e=>e}):void 0,dr="$lit$",ur=`lit$${Math.random().toFixed(9).slice(2)}$`,hr="?"+ur,pr=`<${hr}>`,gr=document,fr=()=>gr.createComment(""),mr=e=>null===e||"object"!=typeof e&&"function"!=typeof e,br=Array.isArray,vr="[ \t\n\f\r]",_r=/<(?:(!--|\/[^a-zA-Z])|(\/?[a-zA-Z][^>\s]*)|(\/?$))/g,yr=/-->/g,Er=/>/g,wr=RegExp(`>|${vr}(?:([^\\s"'>=/]+)(${vr}*=${vr}*(?:[^ \t\n\f\r"'\`<>=]|("|')|))|$)`,"g"),Ar=/'/g,Sr [...]
+     */const dr=globalThis,ur=dr.trustedTypes,hr=ur?ur.createPolicy("lit-html",{createHTML:e=>e}):void 0,pr="$lit$",gr=`lit$${Math.random().toFixed(9).slice(2)}$`,fr="?"+gr,mr=`<${fr}>`,br=document,vr=()=>br.createComment(""),_r=e=>null===e||"object"!=typeof e&&"function"!=typeof e,yr=Array.isArray,wr="[ \t\n\f\r]",Er=/<(?:(!--|\/[^a-zA-Z])|(\/?[a-zA-Z][^>\s]*)|(\/?$))/g,Ar=/-->/g,Sr=/>/g,Lr=RegExp(`>|${wr}(?:([^\\s"'>=/]+)(${wr}*=${wr}*(?:[^ \t\n\f\r"'\`<>=]|("|')|))|$)`,"g"),Or=/'/g,xr [...]
   <button type='button'
       class='tobago-button btn btn-secondary badge' aria-label='deselect ${e}'
       ${s>0?" tabindex='"+String(s)+"'":""}
       @click="${this.removeBadge.bind(this)}"
       @focus="${this.focusEvent.bind(this)}"
-      @blur="${this.blurEvent.bind(this)}"><i class='bi-x-lg'></i></button>`}removeBadge(e){var t;const s=e.target.closest(".btn-group").dataset.tobagoValue,i=this.hiddenSelect.querySelector(`[value="${s}"]`);i.selected=!1;const n=this.badges.querySelector(`[data-tobago-value="${s}"]`),o=n.previousElementSibling,r="SPAN"===(null===(t=n.nextElementSibling)||void 0===t?void 0:t.tagName)?n.nextElementSibling:null;o?o.querySelector("button.btn.badge").focus():r?r.querySelector("button.btn.ba [...]
+      @blur="${this.blurEvent.bind(this)}"><i class='bi-x-lg'></i></button>`}removeBadge(e){var t;const s=e.target.closest(".btn-group").dataset.tobagoValue,i=this.hiddenSelect.querySelector(`[value="${s}"]`);i.selected=!1;const n=this.badges.querySelector(`[data-tobago-value="${s}"]`),o=n.previousElementSibling,r="SPAN"===(null===(t=n.nextElementSibling)||void 0===t?void 0:t.tagName)?n.nextElementSibling:null;o?o.querySelector("button.btn.badge").focus():r?r.querySelector("button.btn.ba [...]
 //# sourceMappingURL=tobago.js.map
diff --git a/tobago-theme/tobago-theme-standard/src/main/js/tobago.js.map b/tobago-theme/tobago-theme-standard/src/main/js/tobago.js.map
index 215016cbfa..08f31547c4 100644
--- a/tobago-theme/tobago-theme-standard/src/main/js/tobago.js.map
+++ b/tobago-theme/tobago-theme-standard/src/main/js/tobago.js.map
@@ -1 +1 @@
-{"version":3,"file":"tobago.js","sources":["../ts/tobago-css.ts","../ts/tobago-key.ts","../ts/tobago-bar.ts","../ts/tobago-date.ts","../ts/tobago-menu-store.ts","../../../../node_modules/@popperjs/core/lib/enums.js","../../../../node_modules/@popperjs/core/lib/dom-utils/getNodeName.js","../../../../node_modules/@popperjs/core/lib/dom-utils/getWindow.js","../../../../node_modules/@popperjs/core/lib/dom-utils/instanceOf.js","../../../../node_modules/@popperjs/core/lib/modifiers/applyStyles [...]
\ No newline at end of file
+{"version":3,"file":"tobago.js","sources":["../ts/tobago-css.ts","../ts/tobago-key.ts","../ts/tobago-bar.ts","../ts/tobago-date.ts","../ts/tobago-menu-store.ts","../../../../node_modules/@popperjs/core/lib/enums.js","../../../../node_modules/@popperjs/core/lib/dom-utils/getNodeName.js","../../../../node_modules/@popperjs/core/lib/dom-utils/getWindow.js","../../../../node_modules/@popperjs/core/lib/dom-utils/instanceOf.js","../../../../node_modules/@popperjs/core/lib/modifiers/applyStyles [...]
\ No newline at end of file
diff --git a/tobago-theme/tobago-theme-standard/src/main/ts/tobago-init.ts b/tobago-theme/tobago-theme-standard/src/main/ts/tobago-init.ts
index baa58f2673..3607fa9419 100644
--- a/tobago-theme/tobago-theme-standard/src/main/ts/tobago-init.ts
+++ b/tobago-theme/tobago-theme-standard/src/main/ts/tobago-init.ts
@@ -31,6 +31,8 @@ import "./tobago-in";
 import "./tobago-messages";
 import "./tobago-overlay";
 import "./tobago-page";
+import "./tobago-paginator-page";
+import "./tobago-paginator-row";
 import "./tobago-panel";
 import "./tobago-popover";
 import "./tobago-popup";
diff --git a/tobago-theme/tobago-theme-standard/src/main/ts/tobago-page.ts b/tobago-theme/tobago-theme-standard/src/main/ts/tobago-page.ts
index 452681c70d..9bd987902e 100644
--- a/tobago-theme/tobago-theme-standard/src/main/ts/tobago-page.ts
+++ b/tobago-theme/tobago-theme-standard/src/main/ts/tobago-page.ts
@@ -52,15 +52,15 @@ export class Page extends HTMLElement {
 
     window.addEventListener("unload", this.beforeUnload.bind(this));
 
-    this.addEventListener("keypress", (event: KeyboardEvent): boolean => {
+    window.addEventListener("keydown", (event: KeyboardEvent): boolean => {
       if (event.key === Key.ENTER) {
         const target = event.target as HTMLElement;
         if (target.tagName === "A" || target.tagName === "BUTTON") {
-          return;
+          return true;
         }
         if (target.tagName === "TEXTAREA") {
           if (!event.metaKey && !event.ctrlKey) {
-            return;
+            return true;
           }
         }
         const name = target.getAttribute("name");
@@ -69,11 +69,11 @@ export class Page extends HTMLElement {
           const command = document.querySelector(`[data-tobago-default='${id}']`);
           if (command) {
             command.dispatchEvent(new MouseEvent("click"));
-            break;
+            return false;
           }
           id = PageStatic.getNamingContainerId(id);
         }
-        return false;
+        document.querySelector("[data-tobago-default]");
       }
     });
   }
diff --git a/tobago-theme/tobago-theme-standard/src/main/ts/tobago-paginator-page.ts b/tobago-theme/tobago-theme-standard/src/main/ts/tobago-paginator-page.ts
new file mode 100644
index 0000000000..1371a85b68
--- /dev/null
+++ b/tobago-theme/tobago-theme-standard/src/main/ts/tobago-paginator-page.ts
@@ -0,0 +1,31 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import {TobagoPaginator} from "./tobago-paginator";
+
+export class TobagoPaginatorPage extends TobagoPaginator {
+
+  constructor() {
+    super();
+  }
+}
+
+document.addEventListener("tobago.init", function (event: Event): void {
+  if (window.customElements.get("tobago-paginator-page") == null) {
+    window.customElements.define("tobago-paginator-page", TobagoPaginatorPage);
+  }
+});
diff --git a/tobago-theme/tobago-theme-standard/src/main/ts/tobago-paginator-row.ts b/tobago-theme/tobago-theme-standard/src/main/ts/tobago-paginator-row.ts
new file mode 100644
index 0000000000..c146aa9d66
--- /dev/null
+++ b/tobago-theme/tobago-theme-standard/src/main/ts/tobago-paginator-row.ts
@@ -0,0 +1,31 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import {TobagoPaginator} from "./tobago-paginator";
+
+export class TobagoPaginatorRow extends TobagoPaginator {
+
+  constructor() {
+    super();
+  }
+}
+
+document.addEventListener("tobago.init", function (event: Event): void {
+  if (window.customElements.get("tobago-paginator-row") == null) {
+    window.customElements.define("tobago-paginator-row", TobagoPaginatorRow);
+  }
+});
diff --git a/tobago-theme/tobago-theme-standard/src/main/ts/tobago-paginator.ts b/tobago-theme/tobago-theme-standard/src/main/ts/tobago-paginator.ts
new file mode 100644
index 0000000000..c2d7a1b794
--- /dev/null
+++ b/tobago-theme/tobago-theme-standard/src/main/ts/tobago-paginator.ts
@@ -0,0 +1,102 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import {Key} from "./tobago-key";
+
+export class TobagoPaginator extends HTMLElement {
+
+  constructor() {
+    super();
+  }
+
+  get text(): HTMLElement {
+    return this.querySelector("span.page-link");
+  }
+
+  get output(): HTMLElement {
+    return this.querySelector(".page-link span");
+  }
+
+  get input(): HTMLInputElement {
+    return this.querySelector(".page-link input");
+  }
+
+  get sheet(): HTMLElement {
+    const sheet: HTMLElement = this.closest("tobago-sheet");
+    if (sheet != null) {
+      return sheet;
+    } else {
+      window.alert("todo: implement find, wenn this is not a child");
+      return document.querySelector("tobago-sheet");
+    }
+  }
+
+  connectedCallback(): void {
+    console.warn("register -------------- click on paging", this.tagName);
+    this.text.addEventListener("click", this.clickOnPaging.bind(this));
+
+    const input = this.input;
+    console.warn("register -------------- blur on paging", this.tagName);
+    input.addEventListener("blur", this.blurPaging.bind(this));
+
+    console.warn("register -------------- keydown on paging", this.tagName);
+    input.addEventListener("keydown", function (event: KeyboardEvent): void {
+      if (event.key === Key.ENTER) {
+        event.stopPropagation();
+        event.preventDefault();
+        event.currentTarget.dispatchEvent(new Event("blur"));
+      }
+    }.bind(this));
+  }
+
+  clickOnPaging(event: MouseEvent): void {
+    const output: HTMLElement = this.output;
+    output.style.display = "none";
+
+    console.warn("execute  -------------- click on paging", this.tagName);
+    const input: HTMLInputElement = this.input;
+    input.style.display = "initial";
+    input.focus();
+    input.select();
+  }
+
+  blurPaging(event: FocusEvent): void {
+    console.warn("execute  -------------- blur on paging", this.tagName);
+    const input = event.currentTarget as HTMLInputElement;
+    const output: HTMLElement = this.output;
+    const number = Number.parseInt(input.value); // sanitizing
+    if (number > 0 && number.toString() !== output.innerHTML) {
+      console.debug("Reloading sheet '%s' old value='%s' new value='%s'", this.id, output.innerHTML, number);
+      output.innerHTML = number.toString();
+      jsf.ajax.request(
+          input.id,
+          null,
+          {
+            params: {
+              "javax.faces.behavior.event": "reload"
+            },
+            execute: this.sheet.id,
+            render: this.sheet.id
+          });
+    } else {
+      console.info("no update needed");
+      input.value = output.innerHTML;
+      input.style.display = "none";
+      output.style.display = "initial";
+    }
+  }
+}
diff --git a/tobago-theme/tobago-theme-standard/src/main/ts/tobago-sheet.ts b/tobago-theme/tobago-theme-standard/src/main/ts/tobago-sheet.ts
index d2e83f3bf9..83a21bc540 100644
--- a/tobago-theme/tobago-theme-standard/src/main/ts/tobago-sheet.ts
+++ b/tobago-theme/tobago-theme-standard/src/main/ts/tobago-sheet.ts
@@ -240,13 +240,17 @@ export class Sheet extends HTMLElement {
 
     // init paging by pages ---------------------------------------------------------------------------------------- //
 
+/*
     for (const pagingText of this.querySelectorAll(".tobago-paging")) {
 
+      console.warn("register ************** click on paging");
       pagingText.addEventListener("click", this.clickOnPaging.bind(this));
 
       const pagingInput = pagingText.querySelector("input");
+      console.warn("register ************** blur on paging");
       pagingInput.addEventListener("blur", this.blurPaging.bind(this));
 
+      console.warn("register ************** keydown on paging");
       pagingInput.addEventListener("keydown", function (event: KeyboardEvent): void {
         if (event.key === Key.ENTER) {
           event.stopPropagation();
@@ -255,6 +259,7 @@ export class Sheet extends HTMLElement {
         }
       });
     }
+*/
   }
 
   // attribute getter + setter ---------------------------------------------------------- //
@@ -888,9 +893,11 @@ Type: ${data.type}`);
     }
   }
 
+/*
   clickOnPaging(event: MouseEvent): void {
     const element = event.currentTarget as HTMLElement;
 
+    console.warn("execute  ************** click on paging");
     const output: HTMLElement = element.querySelector("span");
     output.style.display = "none";
 
@@ -899,8 +906,11 @@ Type: ${data.type}`);
     input.focus();
     input.select();
   }
+*/
 
+/*
   blurPaging(event: FocusEvent): void {
+    console.warn("execute  ************** blur on paging");
     const input = event.currentTarget as HTMLInputElement;
     const output: HTMLElement = input.parentElement.querySelector("span");
     const number = Number.parseInt(input.value); // sanitizing
@@ -924,6 +934,7 @@ Type: ${data.type}`);
       output.style.display = "initial";
     }
   }
+*/
 
   syncScrolling(): void {
     // sync scrolling of body to header
diff --git a/tobago-tool/tobago-tool-apt/src/main/resources/org/apache/myfaces/tobago/apt/component.stg b/tobago-tool/tobago-tool-apt/src/main/resources/org/apache/myfaces/tobago/apt/component.stg
index eca1224b6d..be3ee5b747 100644
--- a/tobago-tool/tobago-tool-apt/src/main/resources/org/apache/myfaces/tobago/apt/component.stg
+++ b/tobago-tool/tobago-tool-apt/src/main/resources/org/apache/myfaces/tobago/apt/component.stg
@@ -439,6 +439,14 @@ GridSpanProperty(property) ::= <<
 <NormalProperty(property)>
 >>
 
+PaginatorModeProperty(property) ::= <<
+<NormalProperty(property)>
+>>
+
+ArrowsProperty(property) ::= <<
+<NormalProperty(property)>
+>>
+
 DoubleProperty(property) ::= <<
 <NormalProperty(property)>
 >>