JSFiddle - React, Tailwind, and code Playground

HTML

<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.6.10/vue.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/vuex/3.1.0/vuex.js"></script>

<div id="app">
  <v-pagination v-model="page" :num-pages="numPages"></v-pagination>
  <v-loader v-if="loading"></v-loader>
  <v-posts v-else :posts="posts"></v-posts>
</div>

CSS

body {
  font-family: monospace;
}

.post {
  border: 1px solid black;
  margin: 5px 0;
  padding: 5px;
}

.post-title {
  margin: 5px 0;
}
.pagination button:not(:disabled) {
  color: red;
  font-weight: bold;
}

JavaScript

const store = new Vuex.Store({
  state: { 
    page: 0,
    perPage: 5,
    total: 0,
    posts: [],
    loading: false,
  },
  getters: {
    numPages: state => Math.ceil(state.total / state.perPage),
  },
  mutations: {
    updateLoading: (state, loading) => state.loading = loading,
    updatePosts: (state, { posts, total, page }) =>
      Object.assign(state, { posts, total, page }),
  },
  actions: {
    async fetchPosts({ state, commit }, page) {
      commit('updateLoading', true);

      const start = (page - 1) * state.perPage;
      const end = page * state.perPage;
      const url = `https://jsonplaceholder.typicode.com/posts?_start=${start}&_end=${end}`;

      try {
        const response = await fetch(url);
        const posts = await response.json();
        const total = response.headers.get('x-total-count');
        commit('updatePosts', { posts, total, page });
      } catch (e) {
        console.error(e);
      }

      commit('updateLoading', false);
    },
  },
});


Vue.component('v-loader', {
  template: `<div>loading...</div>`,
});

Vue.component('v-posts', {
  template: `
<div>
  <div v-for="n in posts" :key="n.id" class="post">
    <h3 class="post-title">#{{ n.id }} {{ n.title }}</h3>
    <div>{{ n.body }}</div>
  </div>
</div>`,
  props: [ 'posts' ],
});

Vue.component('v-pagination', {
  template: `
<div class="pagination">
  <button @click="goTo(1)" :disabled="isFirst">&lt;&lt;</button>
  <button @click="next(-1)" :disabled="isFirst">&lt;</button>
  {{ page }} / {{ numPages }}
  <button @click="next(+1)" :disabled="isLast">&gt;</button>
  <button @click="goTo(numPages)" :disabled="isLast">&gt;&gt;</button>
</div>`,
  model: {
    prop: 'page',
    event: 'paginate',
  },
  props: [ 'page', 'numPages' ],
  computed: {
    isFirst() {
      return this.page <= 1;
    },
    isLast() {
      return this.page >= this.numPages;
    },
  },
  methods: {
    goTo(page) {
      this.$emit('paginate', page);
    },
    next(step) {
     ...