{"id":21905675,"url":"https://github.com/ninsau/nextjs-reusable-table","last_synced_at":"2025-06-19T09:39:22.301Z","repository":{"id":257800668,"uuid":"863215812","full_name":"ninsau/nextjs-reusable-table","owner":"ninsau","description":"A highly customizable and reusable table component for Next.js applications, built with TypeScript and the latest technologies.","archived":false,"fork":false,"pushed_at":"2025-06-14T23:15:11.000Z","size":187,"stargazers_count":3,"open_issues_count":0,"forks_count":0,"subscribers_count":1,"default_branch":"main","last_synced_at":"2025-06-14T23:27:13.312Z","etag":null,"topics":["data-table","next-reusable-component","next-table-component","nextjs-table","reusable-table","reusable-template","table","table-component","tailwind-table","ui-table"],"latest_commit_sha":null,"homepage":"https://nextjs-reusables.vercel.app/tables","language":"TypeScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"0bsd","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/ninsau.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":"CONTRIBUTING.md","funding":null,"license":"LICENSE","code_of_conduct":"CODE_OF_CONDUCT.md","threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null}},"created_at":"2024-09-25T23:19:33.000Z","updated_at":"2025-06-14T23:15:14.000Z","dependencies_parsed_at":null,"dependency_job_id":"3d082971-2eb7-440c-b94a-c56d3c7fdcdf","html_url":"https://github.com/ninsau/nextjs-reusable-table","commit_stats":null,"previous_names":["ninsau/nextjs-reusable-table"],"tags_count":59,"template":false,"template_full_name":null,"purl":"pkg:github/ninsau/nextjs-reusable-table","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ninsau%2Fnextjs-reusable-table","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ninsau%2Fnextjs-reusable-table/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ninsau%2Fnextjs-reusable-table/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ninsau%2Fnextjs-reusable-table/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/ninsau","download_url":"https://codeload.github.com/ninsau/nextjs-reusable-table/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ninsau%2Fnextjs-reusable-table/sbom","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":260726115,"owners_count":23053080,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["data-table","next-reusable-component","next-table-component","nextjs-table","reusable-table","reusable-template","table","table-component","tailwind-table","ui-table"],"created_at":"2024-11-28T16:35:19.242Z","updated_at":"2025-06-19T09:39:17.284Z","avatar_url":"https://github.com/ninsau.png","language":"TypeScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Next.js Reusable Table\n\nA highly customizable and reusable table component for Next.js applications, built with TypeScript and TailwindCSS.\n\n## Installation\n\n```bash\nnpm install nextjs-reusable-table@latest\n# or\nyarn add nextjs-reusable-table@latest\n# or\npnpm add nextjs-reusable-table@latest\n```\n\n## Prerequisites\n\n- Next.js 12 or later\n- React 16 or later\n- React DOM 16 or later\n- Tailwind CSS 3.0 or later\n- TypeScript (recommended)\n\n`Note: This is a Client Component (\"use client\"). Using it in purely SSR contexts may require additional handling to avoid hydration mismatches.`\n\n## Basic Usage\n\n```tsx\n\"use client\";\nimport React from \"react\";\nimport { TableComponent } from \"nextjs-reusable-table\";\nimport \"nextjs-reusable-table/dist/index.css\";\n\ninterface User {\n  id: number;\n  name: string;\n}\n\nexport default function BasicTable() {\n  const data: User[] = [\n    { id: 1, name: \"Alice\" },\n    { id: 2, name: \"Bob\" },\n  ];\n\n  return (\n    \u003cTableComponent\u003cUser\u003e\n      columns={[\"ID\", \"Name\"]}\n      data={data}\n      props={[\"id\", \"name\"]}\n    /\u003e\n  );\n}\n```\n\n## Introduction\n\nNext.js Reusable Table is designed for easy integration into Next.js apps. It supports sorting, pagination, row actions, and flexible data rendering via custom formatters. Built-in helpers handle arrays, dates, and URLs gracefully.\n\n## Features\n\n### Column Management\n\n- Hide/Show columns: Each column header has a dropdown (⋮) to remove or unhide columns.\n- Sorting: Specify which columns can be sorted.\n\n```tsx\n\u003cTableComponent\n  columns={[\"Name\", \"Email\"]}\n  data={yourData}\n  props={[\"name\", \"email\"]}\n  sortableProps={[\"name\"]}\n/\u003e\n```\n\n### Smart Row Interactions\n\nThe table provides intelligent click handling:\n\n- Click anywhere on a row to trigger row action\n- Click on cell content to expand/interact without triggering row action\n- Expandable content with \"show more\" functionality\n\n### Built-In Data Handling\n\n- Dates automatically formatted.\n- Arrays displayed as chips with “+X more” for large arrays.\n- URLs automatically detected and rendered as links.\n\n### Action Dropdowns\n\n- Easily attach row actions via a dropdown button:\n\n```tsx\n\u003cTableComponent\n  actions\n  actionTexts={[\"Edit\", \"Delete\"]}\n  actionFunctions={[(item) =\u003e editItem(item), (item) =\u003e deleteItem(item)]}\n/\u003e\n```\n\n### Search and Pagination\n\n- searchValue filters rows against all columns.\n- Built-in pagination. Provide page, setPage, and itemsPerPage.\n\n```tsx\n\u003cTableComponent\n  searchValue={searchTerm}\n  enablePagination\n  page={page}\n  setPage={setPage}\n  itemsPerPage={10}\n/\u003e\n```\n\n### Custom Styling\n\n- Styling with Tailwind. Override default classes or disable them entirely:\n\n```tsx\nconst customClassNames = {\n  table: \"my-custom-table-styles\",\n  thead: \"bg-gray-200 text-gray-700\",\n  tbody: \"divide-y divide-gray-200\",\n  pagination: {\n    container: \"flex justify-center mt-4\",\n    button: \"px-2 py-1 border\",\n    pageInfo: \"mx-2\",\n  },\n};\n\n\u003cTableComponent\n  customClassNames={customClassNames}\n  disableDefaultStyles={false}\n/\u003e;\n```\n\n### Dark Mode\n\n- Automatically respects system preference if enableDarkMode is true.\n\n### Loading Skeleton\n\n- Show a skeleton loader while data is loading.\n\n```tsx\n\u003cTableComponent loading /\u003e\n```\n\n### Empty State\n\nPass noContentProps to customize text and icon:\n\n```tsx\n\u003cTableComponent\n  noContentProps={{\n    text: \"No data found\",\n    icon: \u003cMyCustomIcon /\u003e,\n  }}\n/\u003e\n```\n\n## Advanced Example\n\n```tsx\n\"use client\";\nimport React, { useState, useMemo } from \"react\";\nimport { TableComponent } from \"nextjs-reusable-table\";\nimport \"nextjs-reusable-table/dist/index.css\";\n\ninterface Project {\n  id: number;\n  title: string;\n  tags: string[];\n  deadline: string;\n  active: boolean;\n  link: string;\n}\n\nexport default function AdvancedProjectTable() {\n  const initialData: Project[] = [\n    {\n      id: 1,\n      title: \"Website Redesign\",\n      tags: [\"UI\", \"UX\", \"Frontend\"],\n      deadline: \"2025-03-15T10:30:00Z\",\n      active: true,\n      link: \"https://example.com/project/1\",\n    },\n    {\n      id: 2,\n      title: \"Mobile App Development\",\n      tags: [\"iOS\", \"Android\", \"Backend\"],\n      deadline: \"2025-04-01T14:00:00Z\",\n      active: false,\n      link: \"https://example.com/project/2\",\n    },\n    {\n      id: 3,\n      title: \"Marketing Campaign\",\n      tags: [\"SEO\", \"Social Media\"],\n      deadline: \"2025-02-20T09:00:00Z\",\n      active: true,\n      link: \"https://example.com/project/3\",\n    },\n    {\n      id: 4,\n      title: \"E-commerce Platform\",\n      tags: [\"Frontend\", \"Backend\", \"API\", \"Payments\", \"Analytics\"],\n      deadline: \"2025-05-05T11:00:00Z\",\n      active: true,\n      link: \"https://example.com/project/4\",\n    },\n    {\n      id: 5,\n      title: \"Data Analysis\",\n      tags: [\"Python\", \"ML\", \"Data Science\"],\n      deadline: \"2025-03-01T08:00:00Z\",\n      active: false,\n      link: \"https://example.com/project/5\",\n    },\n  ];\n\n  const [projects, setProjects] = useState\u003cProject[]\u003e(initialData);\n  const [page, setPage] = useState\u003cnumber\u003e(1);\n  const [searchTerm, setSearchTerm] = useState\u003cstring\u003e(\"\");\n  const [sortConfig, setSortConfig] = useState\u003c{\n    prop: keyof Project;\n    order: \"asc\" | \"desc\";\n  } | null\u003e(null);\n\n  const handleSort = (prop: keyof Project) =\u003e {\n    let order: \"asc\" | \"desc\" = \"asc\";\n    if (sortConfig \u0026\u0026 sortConfig.prop === prop) {\n      order = sortConfig.order === \"asc\" ? \"desc\" : \"asc\";\n    }\n    setSortConfig({ prop, order });\n  };\n\n  const editProject = (project: Project) =\u003e {\n    alert(`Edit project: ${project.title}`);\n  };\n\n  const deleteProject = (project: Project) =\u003e {\n    alert(`Delete project: ${project.title}`);\n  };\n\n  const handleRowClick = (project: Project) =\u003e {\n    console.log(\"Row clicked:\", project);\n  };\n\n  const formatValue = (value: string, prop: string, project: Project) =\u003e {\n    if (prop === \"active\") {\n      return project.active ? \"Active\" : \"Archived\";\n    }\n    return value;\n  };\n\n  const formatHeader = (header: string, prop: string, index: number) =\u003e (\n    \u003cdiv\u003e\n      \u003cspan className=\"font-semibold uppercase tracking-wide\"\u003e{header}\u003c/span\u003e\n    \u003c/div\u003e\n  );\n\n  const sortedFilteredProjects = useMemo(() =\u003e {\n    const filtered = projects.filter((project) =\u003e {\n      const searchLower = searchTerm.toLowerCase();\n      return (\n        String(project.id).includes(searchLower) ||\n        project.title.toLowerCase().includes(searchLower) ||\n        project.tags.join(\" \").toLowerCase().includes(searchLower) ||\n        project.deadline.toLowerCase().includes(searchLower) ||\n        (project.active ? \"active\" : \"archived\").includes(searchLower) ||\n        project.link.toLowerCase().includes(searchLower)\n      );\n    });\n    if (sortConfig) {\n      filtered.sort((a, b) =\u003e {\n        const aValue = String(a[sortConfig.prop]).toLowerCase();\n        const bValue = String(b[sortConfig.prop]).toLowerCase();\n        if (aValue \u003c bValue) return sortConfig.order === \"asc\" ? -1 : 1;\n        if (aValue \u003e bValue) return sortConfig.order === \"asc\" ? 1 : -1;\n        return 0;\n      });\n    }\n    return filtered;\n  }, [projects, searchTerm, sortConfig]);\n\n  const customClassNames = {\n    table: \"border border-gray-300 rounded-md shadow-sm\",\n    thead: \"bg-blue-50 text-blue-700\",\n    tbody: \"\",\n    th: \"px-4 py-2\",\n    tr: \"\",\n    td: \"px-4 py-2\",\n    pagination: {\n      container: \"mt-4\",\n      button: \"bg-blue-500 text-white rounded px-3 py-1\",\n      buttonDisabled: \"bg-gray-300 text-gray-700 rounded px-3 py-1\",\n      pageInfo: \"text-blue-700\",\n    },\n  };\n\n  return (\n    \u003cdiv className=\"p-4\"\u003e\n      \u003ch1 className=\"text-2xl font-bold mb-4\"\u003eAdvanced Project Table\u003c/h1\u003e\n      \u003cdiv className=\"mb-4\"\u003e\n        \u003cinput\n          type=\"text\"\n          placeholder=\"Search projects...\"\n          value={searchTerm}\n          onChange={(e) =\u003e setSearchTerm(e.target.value)}\n          className=\"border border-gray-300 rounded px-2 py-1 w-full\"\n        /\u003e\n      \u003c/div\u003e\n      \u003cTableComponent\u003cProject\u003e\n        columns={[\"ID\", \"Title\", \"Tags\", \"Deadline\", \"Status\", \"Link\"]}\n        data={sortedFilteredProjects}\n        props={[\"id\", \"title\", \"tags\", \"deadline\", \"active\", \"link\"]}\n        sortableProps={[\"title\", \"deadline\"]}\n        onSort={handleSort}\n        actionTexts={[\"Edit\", \"Delete\"]}\n        actionFunctions={[editProject, deleteProject]}\n        rowOnClick={handleRowClick}\n        formatValue={formatValue}\n        formatHeader={formatHeader}\n        enablePagination\n        page={page}\n        setPage={setPage}\n        itemsPerPage={2}\n        noContentProps={{ text: \"No projects found\", icon: null }}\n        customClassNames={customClassNames}\n      /\u003e\n    \u003c/div\u003e\n  );\n}\n```\n\n## Prop Reference\n\n| Prop                 | Type                                 | Default | Description                                                  |\n| -------------------- | ------------------------------------ | ------- | ------------------------------------------------------------ |\n| columns              | string[]                             | –       | Column headers                                               |\n| data                 | T[]                                  | –       | Array of data objects                                        |\n| props                | ReadonlyArray\u003ckeyof T\u003e               | –       | Object keys to display                                       |\n| actions              | boolean                              | false   | Enable action dropdown                                       |\n| actionTexts          | string[]                             | –       | Labels for dropdown actions                                  |\n| loading              | boolean                              | false   | Show loading skeleton                                        |\n| actionFunctions      | Array\u003cFunction\u003e                      | –       | Handlers for dropdown items                                  |\n| searchValue          | string                               | –       | Filter rows by substring                                     |\n| rowOnClick           | (item: T) =\u003e void                    | –       | Callback for row clicks                                      |\n| enablePagination     | boolean                              | false   | Enable pagination                                            |\n| page                 | number                               | 1       | Current page index                                           |\n| setPage              | (page: number) =\u003e void               | –       | Page setter callback                                         |\n| itemsPerPage         | number                               | 10      | Rows per page                                                |\n| totalPages           | number                               | –       | Override total pages                                         |\n| sortableProps        | Array\u003ckeyof T\u003e                       | []      | Columns that can be sorted                                   |\n| formatValue          | (val, prop, item) =\u003e React.ReactNode | –       | Custom cell formatter                                        |\n| enableDarkMode       | boolean                              | true    | Respect system dark mode                                     |\n| disableDefaultStyles | boolean                              | false   | Disable built-in styling                                     |\n| customClassNames     | object                               | {}      | Tailwind class overrides                                     |\n| noContentProps       | object                               | {}      | Custom empty state                                           |\n| onSort               | (prop: keyof T) =\u003e void              | –       | Callback triggered when a sortable column header is clicked. |\n| formatHeader         | (header: string) =\u003e React.ReactNode  | –       | Custom header formatter                                      |\n\n## Contributing\n\nContributions are welcome! Please see [CONTRIBUTING.md](CONTRIBUTING.md) for details on how to get started.\n\n## Versioning\n\nWe use [Semantic Versioning](https://semver.org/) for versioning. For the versions available, see the [tags on this repository](https://github.com/ninsau/nextjs-reusable-table/tags).\n\nTo bump the version, update the `version` field in `package.json` and follow the guidelines in the [CONTRIBUTING.md](CONTRIBUTING.md) file.\n\n## License\n\nThis project is licensed under the ISC License - see the [LICENSE](LICENSE) file for details.\n\n## Code of Conduct\n\nThis project adheres to the [Contributor Covenant Code of Conduct](CODE_OF_CONDUCT.md). By participating, you are expected to uphold this code.\n\n## Acknowledgments\n\n- Inspired by common data table patterns in React and Next.js applications.\n- Thanks to all contributors and users for their support.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fninsau%2Fnextjs-reusable-table","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fninsau%2Fnextjs-reusable-table","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fninsau%2Fnextjs-reusable-table/lists"}