├── .all-contributorsrc ├── .changeset ├── README.md └── config.json ├── .editorconfig ├── .github ├── CODE_OF_CONDUCT.md ├── CONTRIBUTING.md ├── FUNDING.yml ├── ISSUE_TEMPLATE │ ├── bug-report.yml │ ├── config.yml │ └── update-docs.yml ├── screenshot.png └── workflows │ ├── ci.yml │ └── update-algolia-index.yml ├── .gitignore ├── .prettierignore ├── .prettierrc ├── .vscode └── settings.json ├── LICENSE ├── README.md ├── apps └── www │ ├── .eslintrc.cjs │ ├── .gitignore │ ├── CHANGELOG.md │ ├── env.js │ ├── next-sitemap.config.js │ ├── next.config.js │ ├── package.json │ ├── postcss.config.cjs │ ├── public │ ├── android-chrome-192x192.png │ ├── android-chrome-512x512.png │ ├── apple-touch-icon.png │ ├── favicon-16x16.png │ ├── favicon-32x32.png │ ├── favicon.ico │ └── site.webmanifest │ ├── src │ ├── app │ │ ├── (docs) │ │ │ ├── introduction │ │ │ │ └── page.tsx │ │ │ ├── layout.tsx │ │ │ ├── migrate-to-v3 │ │ │ │ └── page.tsx │ │ │ └── react-hook │ │ │ │ └── [slug] │ │ │ │ └── page.tsx │ │ ├── (marketing) │ │ │ ├── layout.tsx │ │ │ └── page.tsx │ │ ├── globals.css │ │ ├── layout.tsx │ │ └── prism.css │ ├── assets │ │ └── fonts │ │ │ ├── CalSans-SemiBold.ttf │ │ │ ├── CalSans-SemiBold.woff │ │ │ └── CalSans-SemiBold.woff2 │ ├── components │ │ ├── buy-me-a-coffee.tsx │ │ ├── carbon-ads │ │ │ ├── ads.tsx │ │ │ ├── index.ts │ │ │ └── use-script.ts │ │ ├── command-copy.tsx │ │ ├── doc-search │ │ │ ├── command-menu.tsx │ │ │ ├── doc-search.tsx │ │ │ ├── footer.tsx │ │ │ ├── hits.tsx │ │ │ ├── index.ts │ │ │ ├── input.tsx │ │ │ ├── modal.context.tsx │ │ │ ├── open-button.tsx │ │ │ ├── types.ts │ │ │ └── use-cmd-k.ts │ │ ├── docs │ │ │ ├── left-sidebar.tsx │ │ │ ├── page-header.tsx │ │ │ ├── pager.tsx │ │ │ ├── right-sidebar.tsx │ │ │ └── table-of-content.tsx │ │ ├── main-nav.tsx │ │ ├── mobile-nav.tsx │ │ └── ui │ │ │ ├── button.tsx │ │ │ ├── command.tsx │ │ │ ├── components.tsx │ │ │ ├── dialog.tsx │ │ │ ├── dropdown-menu.tsx │ │ │ └── icons.tsx │ ├── config │ │ ├── docs.ts │ │ ├── marketing.ts │ │ └── site.ts │ ├── lib │ │ ├── api.ts │ │ └── utils.ts │ └── types │ │ └── index.ts │ ├── tailwind.config.js │ └── tsconfig.json ├── package.json ├── packages ├── eslint-config-custom │ ├── .eslintrc.cjs │ ├── .gitignore │ ├── CHANGELOG.md │ ├── README.md │ ├── index.cjs │ └── package.json └── usehooks-ts │ ├── .eslintrc.cjs │ ├── .gitignore │ ├── CHANGELOG.md │ ├── README.md │ ├── package.json │ ├── src │ ├── index.ts │ ├── useBoolean │ │ ├── index.ts │ │ ├── useBoolean.demo.tsx │ │ ├── useBoolean.md │ │ ├── useBoolean.test.ts │ │ └── useBoolean.ts │ ├── useClickAnyWhere │ │ ├── index.ts │ │ ├── useClickAnyWhere.demo.tsx │ │ ├── useClickAnyWhere.md │ │ ├── useClickAnyWhere.test.ts │ │ └── useClickAnyWhere.ts │ ├── useCopyToClipboard │ │ ├── index.ts │ │ ├── useCopyToClipboard.demo.tsx │ │ ├── useCopyToClipboard.md │ │ ├── useCopyToClipboard.test.ts │ │ └── useCopyToClipboard.ts │ ├── useCountdown │ │ ├── index.ts │ │ ├── useCountdown.demo.tsx │ │ ├── useCountdown.md │ │ ├── useCountdown.test.ts │ │ └── useCountdown.ts │ ├── useCounter │ │ ├── index.ts │ │ ├── useCounter.demo.tsx │ │ ├── useCounter.md │ │ ├── useCounter.test.ts │ │ └── useCounter.ts │ ├── useDarkMode │ │ ├── index.ts │ │ ├── useDarkMode.demo.tsx │ │ ├── useDarkMode.md │ │ ├── useDarkMode.test.ts │ │ └── useDarkMode.ts │ ├── useDebounceCallback │ │ ├── index.ts │ │ ├── useDebounceCallback.demo.tsx │ │ ├── useDebounceCallback.md │ │ ├── useDebounceCallback.test.ts │ │ └── useDebounceCallback.ts │ ├── useDebounceValue │ │ ├── index.ts │ │ ├── useDebounceValue.demo.tsx │ │ ├── useDebounceValue.md │ │ ├── useDebounceValue.test.ts │ │ └── useDebounceValue.ts │ ├── useDocumentTitle │ │ ├── index.ts │ │ ├── useDocumentTitle.demo.tsx │ │ ├── useDocumentTitle.md │ │ ├── useDocumentTitle.test.ts │ │ └── useDocumentTitle.ts │ ├── useEventCallback │ │ ├── index.ts │ │ ├── useEventCallback.demo.tsx │ │ ├── useEventCallback.md │ │ ├── useEventCallback.test.tsx │ │ └── useEventCallback.ts │ ├── useEventListener │ │ ├── index.ts │ │ ├── useEventListener.demo.tsx │ │ ├── useEventListener.md │ │ ├── useEventListener.test.ts │ │ └── useEventListener.ts │ ├── useHover │ │ ├── index.ts │ │ ├── useHover.demo.tsx │ │ ├── useHover.md │ │ ├── useHover.test.ts │ │ └── useHover.ts │ ├── useIntersectionObserver │ │ ├── index.ts │ │ ├── useIntersectionObserver.demo.tsx │ │ ├── useIntersectionObserver.md │ │ └── useIntersectionObserver.ts │ ├── useInterval │ │ ├── index.ts │ │ ├── useInterval.demo.tsx │ │ ├── useInterval.md │ │ ├── useInterval.test.ts │ │ └── useInterval.ts │ ├── useIsClient │ │ ├── index.ts │ │ ├── useIsClient.demo.tsx │ │ ├── useIsClient.md │ │ ├── useIsClient.test.ts │ │ └── useIsClient.ts │ ├── useIsMounted │ │ ├── index.ts │ │ ├── useIsMounted.demo.tsx │ │ ├── useIsMounted.md │ │ ├── useIsMounted.test.ts │ │ └── useIsMounted.ts │ ├── useIsomorphicLayoutEffect │ │ ├── index.ts │ │ ├── useIsomorphicLayoutEffect.demo.tsx │ │ ├── useIsomorphicLayoutEffect.md │ │ └── useIsomorphicLayoutEffect.ts │ ├── useLocalStorage │ │ ├── index.ts │ │ ├── useLocalStorage.demo.tsx │ │ ├── useLocalStorage.md │ │ ├── useLocalStorage.test.ts │ │ └── useLocalStorage.ts │ ├── useMap │ │ ├── index.ts │ │ ├── useMap.demo.tsx │ │ ├── useMap.md │ │ ├── useMap.test.ts │ │ └── useMap.ts │ ├── useMediaQuery │ │ ├── index.ts │ │ ├── useMediaQuery.demo.tsx │ │ ├── useMediaQuery.md │ │ └── useMediaQuery.ts │ ├── useOnClickOutside │ │ ├── index.ts │ │ ├── useOnClickOuside.test.ts │ │ ├── useOnClickOutside.demo.tsx │ │ ├── useOnClickOutside.md │ │ └── useOnClickOutside.ts │ ├── useReadLocalStorage │ │ ├── index.ts │ │ ├── useReadLocalStorage.demo.tsx │ │ ├── useReadLocalStorage.md │ │ ├── useReadLocalStorage.test.ts │ │ └── useReadLocalStorage.ts │ ├── useResizeObserver │ │ ├── index.ts │ │ ├── useResizeObserver.demo.tsx │ │ ├── useResizeObserver.md │ │ ├── useResizeObserver.test.tsx │ │ └── useResizeObserver.ts │ ├── useScreen │ │ ├── index.ts │ │ ├── useScreen.demo.tsx │ │ ├── useScreen.md │ │ └── useScreen.ts │ ├── useScript │ │ ├── index.ts │ │ ├── useScript.demo.tsx │ │ ├── useScript.md │ │ ├── useScript.test.ts │ │ └── useScript.ts │ ├── useScrollLock │ │ ├── index.ts │ │ ├── useScrollLock.demo.tsx │ │ ├── useScrollLock.md │ │ ├── useScrollLock.test.ts │ │ └── useScrollLock.ts │ ├── useSessionStorage │ │ ├── index.ts │ │ ├── useSessionStorage.demo.tsx │ │ ├── useSessionStorage.md │ │ ├── useSessionStorage.test.ts │ │ └── useSessionStorage.ts │ ├── useStep │ │ ├── index.ts │ │ ├── useStep.demo.tsx │ │ ├── useStep.md │ │ ├── useStep.test.ts │ │ └── useStep.ts │ ├── useTernaryDarkMode │ │ ├── index.ts │ │ ├── useTernaryDarkMode.demo.tsx │ │ ├── useTernaryDarkMode.md │ │ ├── useTernaryDarkMode.test.ts │ │ └── useTernaryDarkMode.ts │ ├── useTimeout │ │ ├── index.ts │ │ ├── useTimeout.demo.tsx │ │ ├── useTimeout.md │ │ ├── useTimeout.test.ts │ │ └── useTimeout.ts │ ├── useToggle │ │ ├── index.ts │ │ ├── useToggle.demo.tsx │ │ ├── useToggle.md │ │ ├── useToggle.test.ts │ │ └── useToggle.ts │ ├── useUnmount │ │ ├── index.ts │ │ ├── useUnmount.demo.tsx │ │ ├── useUnmount.md │ │ ├── useUnmount.test.ts │ │ └── useUnmount.ts │ └── useWindowSize │ │ ├── index.ts │ │ ├── useWindowSize.demo.tsx │ │ ├── useWindowSize.md │ │ ├── useWindowSize.test.ts │ │ └── useWindowSize.ts │ ├── tests │ ├── mocks.ts │ └── setup.ts │ ├── tsconfig.json │ ├── tsup.config.ts │ └── vitest.config.ts ├── pnpm-lock.yaml ├── pnpm-workspace.yaml ├── renovate.json ├── scripts ├── env.js ├── generate-doc.js ├── update-algolia-index.js ├── update-testing-issue.js └── utils │ ├── data-transform.js │ ├── generate-doc-files.js │ ├── get-hooks.js │ ├── get-markdown-data.js │ └── update-readme.js ├── turbo.json ├── turbo └── generators │ ├── config.cts │ └── templates │ ├── hook │ ├── hook.demo.tsx.hbs │ ├── hook.mdx.hbs │ ├── hook.test.ts.hbs │ ├── hook.ts.hbs │ └── index.ts.hbs │ └── index.ts.hbs └── typedoc.json /.changeset/README.md: -------------------------------------------------------------------------------- 1 | # Changesets 2 | 3 | Hello and welcome! This folder has been automatically generated by `@changesets/cli`, a build tool that works 4 | with multi-package repos, or single-package repos to help you version and publish your code. You can 5 | find the full documentation for it [in our repository](https://github.com/changesets/changesets) 6 | 7 | We have a quick list of common questions to get you started engaging with this project in 8 | [our documentation](https://github.com/changesets/changesets/blob/main/docs/common-questions.md) 9 | -------------------------------------------------------------------------------- /.changeset/config.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://unpkg.com/@changesets/config@2.3.0/schema.json", 3 | "changelog": "@changesets/cli/changelog", 4 | "commit": false, 5 | "fixed": [], 6 | "linked": [], 7 | "access": "public", 8 | "baseBranch": "master", 9 | "updateInternalDependencies": "patch", 10 | "ignore": [] 11 | } 12 | -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | # editorconfig.org 2 | 3 | root = true 4 | 5 | [*] 6 | indent_style = space 7 | indent_size = 2 8 | charset = utf-8 9 | trim_trailing_whitespace = true 10 | insert_final_newline = true 11 | -------------------------------------------------------------------------------- /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | # These are supported funding model platforms 2 | 3 | github: [juliencrn] 4 | # patreon: # Replace with a single Patreon username 5 | # open_collective: #usehooks-ts # Replace with a single Open Collective username 6 | # ko_fi: # Replace with a single Ko-fi username 7 | # tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel 8 | # community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry 9 | # liberapay: # Replace with a single Liberapay username 10 | # issuehunt: # Replace with a single IssueHunt username 11 | # otechie: # Replace with a single Otechie username 12 | # lfx_crowdfunding: # Replace with a single LFX Crowdfunding project-name e.g., cloud-foundry 13 | custom: [ 14 | "https://juliencaron.com/donate", 15 | "https://www.paypal.com/paypalme/juliencrn", 16 | "https://buy.stripe.com/fZefZY8Bv32cg9O3cc", 17 | "https://www.buymeacoffee.com/juliencrn" 18 | ] 19 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/bug-report.yml: -------------------------------------------------------------------------------- 1 | name: 'Bug report 🐞' 2 | description: >- 3 | Create a report to help us improve 4 | title: '[BUG]' 5 | labels: 6 | - bug 7 | body: 8 | - type: textarea 9 | id: describe_bug 10 | attributes: 11 | label: Describe the bug 12 | description: A clear and concise description of what the bug is. Include any error messages or unexpected behaviors. 13 | validations: 14 | required: true 15 | 16 | - type: textarea 17 | id: reproduce_steps 18 | attributes: 19 | label: To Reproduce 20 | description: Outline the steps to reproduce the issue. Be specific and include details like browser, OS, or any relevant configurations. 21 | validations: 22 | required: true 23 | 24 | - type: textarea 25 | id: expected_behavior 26 | attributes: 27 | label: Expected behavior 28 | description: Explain what you expected to happen. 29 | validations: 30 | required: true 31 | 32 | - type: textarea 33 | id: additional_context 34 | attributes: 35 | label: Additional context 36 | description: Include any other relevant information that might help understand the issue. 37 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/config.yml: -------------------------------------------------------------------------------- 1 | blank_issues_enabled: false 2 | contact_links: 3 | - name: View documentation 📚 4 | url: https://usehooks-ts.com/ 5 | about: Check out the official docs for answers to common questions. 6 | - name: Feature requests 💡 7 | url: https://github.com/juliencrn/usehooks-ts/discussions/categories/ideas 8 | about: Suggest a new React hook idea. 9 | - name: Questions 💭 10 | url: https://github.com/juliencrn/usehooks-ts/discussions/categories/help 11 | about: Need support with a React hook problem? Open up a help request. 12 | - name: Donate ❤️ 13 | url: https://github.com/sponsors/juliencrn 14 | about: Love usehooks-ts? Show your support by donating today! 15 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/update-docs.yml: -------------------------------------------------------------------------------- 1 | name: Update docs ✍️ 2 | description: >- 3 | Find a mistake in our documentation, or have a suggestion to improve them? Let us know here. 4 | labels: 5 | - documentation 6 | body: 7 | - type: textarea 8 | id: description 9 | attributes: 10 | label: Describe the problem 11 | description: A clear and concise description of what is wrong in the documentation or what you would like to improve. Please include URLs to the pages you're referring to. 12 | validations: 13 | required: true 14 | - type: textarea 15 | id: context 16 | attributes: 17 | label: Additional context 18 | description: Add any other context about the problem here. 19 | -------------------------------------------------------------------------------- /.github/screenshot.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/juliencrn/usehooks-ts/61949134144d3690fe9f521260a16c779a6d3797/.github/screenshot.png -------------------------------------------------------------------------------- /.github/workflows/ci.yml: -------------------------------------------------------------------------------- 1 | name: Build and test 2 | 3 | on: 4 | push: 5 | branches: 6 | - 'master' 7 | pull_request: 8 | 9 | env: 10 | NODE_VERSION: 20 11 | PNPM_VERSION: 8 12 | 13 | jobs: 14 | test: 15 | runs-on: ubuntu-latest 16 | timeout-minutes: 15 17 | steps: 18 | - name: Checkout code 19 | uses: actions/checkout@v4 20 | with: 21 | fetch-depth: 2 22 | 23 | - name: Setup Pnpm 24 | uses: pnpm/action-setup@v3 25 | with: 26 | version: ${{ env.PNPM_VERSION }} 27 | 28 | - name: Setup Node.js environment 29 | uses: actions/setup-node@v4 30 | with: 31 | node-version: ${{ env.NODE_VERSION }} 32 | cache: 'pnpm' 33 | cache-dependency-path: '**/pnpm-lock.yaml' 34 | 35 | - name: Install dependencies 36 | run: pnpm install --frozen-lockfile 37 | 38 | - name: Build 39 | run: pnpm build 40 | 41 | - name: Lint 42 | run: pnpm lint 43 | 44 | - name: Test 45 | run: pnpm test 46 | -------------------------------------------------------------------------------- /.github/workflows/update-algolia-index.yml: -------------------------------------------------------------------------------- 1 | name: Update Algolia index 2 | 3 | on: 4 | push: 5 | branches: 6 | - master 7 | 8 | env: 9 | NODE_VERSION: 20 10 | PNPM_VERSION: 8 11 | 12 | jobs: 13 | run: 14 | runs-on: ubuntu-latest 15 | timeout-minutes: 15 16 | steps: 17 | - name: Checkout code 18 | uses: actions/checkout@v4 19 | with: 20 | fetch-depth: 2 21 | 22 | - name: Setup Pnpm 23 | uses: pnpm/action-setup@v3 24 | with: 25 | version: ${{ env.PNPM_VERSION }} 26 | 27 | - name: Setup Node.js environment 28 | uses: actions/setup-node@v4 29 | with: 30 | node-version: ${{ env.NODE_VERSION }} 31 | cache: 'pnpm' 32 | cache-dependency-path: '**/pnpm-lock.yaml' 33 | 34 | - name: Install dependencies 35 | run: pnpm install --frozen-lockfile 36 | 37 | - name: Build 38 | run: pnpm build 39 | 40 | - name: Generate documentation from JSDoc 41 | run: pnpm generate-doc 42 | 43 | - name: Update Algolia index 44 | env: 45 | ALGOLIA_APP_ID: ${{ secrets.ALGOLIA_APP_ID }} 46 | ALGOLIA_ADMIN_KEY: ${{ secrets.ALGOLIA_ADMIN_KEY }} 47 | run: pnpm update-algolia-index 48 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | 3 | # Logs 4 | logs 5 | *.log 6 | npm-debug.log* 7 | yarn-debug.log* 8 | yarn-error.log* 9 | npm-debug.log* 10 | *.tsbuildinfo 11 | 12 | # Cache 13 | .npm 14 | .cache 15 | .eslintcache 16 | .turbo 17 | 18 | # Compiled stuff 19 | dist 20 | generated 21 | 22 | # Coverage 23 | coverage 24 | 25 | # dotenv environment variable files 26 | .env* 27 | !.env.example 28 | 29 | # Output of 'npm pack' 30 | *.tgz 31 | 32 | # Mac files 33 | .DS_Store 34 | 35 | # Local Netlify folder 36 | .netlify 37 | -------------------------------------------------------------------------------- /.prettierignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | templates 3 | dist 4 | .turbo 5 | public 6 | .cache 7 | pnpm-lock.yaml 8 | .changeset 9 | .github 10 | apps/www/.next 11 | -------------------------------------------------------------------------------- /.prettierrc: -------------------------------------------------------------------------------- 1 | { 2 | "arrowParens": "avoid", 3 | "semi": false, 4 | "printWidth": 80, 5 | "singleQuote": true, 6 | "tabWidth": 2, 7 | "trailingComma": "all" 8 | } 9 | -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "editor.formatOnSave": true, 3 | "editor.tabSize": 2, 4 | "files.encoding": "utf8", 5 | "files.trimTrailingWhitespace": true, 6 | "files.insertFinalNewline": true, 7 | "search.exclude": { 8 | "public/**": true, 9 | "node_modules/**": true, 10 | "coverage/**": true, 11 | "dist/**": true, 12 | "generated/**": true, 13 | ".cache/**": true, 14 | ".git/**": true, 15 | "**/package-lock.json": true, 16 | "**/pnpm-lock.yaml": true 17 | }, 18 | "eslint.validate": [ 19 | "javascript", 20 | "javascriptreact", 21 | "typescript", 22 | "typescriptreact" 23 | ], 24 | "eslint.format.enable": true, 25 | "editor.codeActionsOnSave": { 26 | "source.fixAll.eslint": "explicit", 27 | "source.fixAll": "explicit" 28 | }, 29 | "editor.defaultFormatter": "esbenp.prettier-vscode", 30 | "cSpell.words": [ 31 | "algolia", 32 | "clsx", 33 | "cmdk", 34 | "contentlayer", 35 | "fira", 36 | "frontmatter", 37 | "gtag", 38 | "juliencrn", 39 | "lucide", 40 | "nextjs", 41 | "okaidia", 42 | "prismjs", 43 | "rehype", 44 | "tailwindcss", 45 | "UMAMI", 46 | "usehooks" 47 | ], 48 | "eslint.workingDirectories": [ 49 | { 50 | "directory": "apps/www", 51 | "changeProcessCWD": true 52 | }, 53 | { 54 | "directory": "packages/eslint-config-custom", 55 | "changeProcessCWD": true 56 | }, 57 | { 58 | "directory": "packages/usehooks-ts", 59 | "changeProcessCWD": true 60 | } 61 | ], 62 | "[jsonc]": { 63 | "editor.defaultFormatter": "esbenp.prettier-vscode" 64 | }, 65 | "[json]": { 66 | "editor.defaultFormatter": "esbenp.prettier-vscode" 67 | }, 68 | "[yaml]": { 69 | "editor.defaultFormatter": "esbenp.prettier-vscode" 70 | }, 71 | "[typescript]": { 72 | "editor.defaultFormatter": "dbaeumer.vscode-eslint" 73 | }, 74 | "[typescriptreact]": { 75 | "editor.defaultFormatter": "dbaeumer.vscode-eslint" 76 | }, 77 | "files.associations": { "*.json": "jsonc" } 78 | } 79 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2020 Julien CARON 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | 23 | -------------------------------------------------------------------------------- /apps/www/.eslintrc.cjs: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | extends: ['next/core-web-vitals', 'custom'], 3 | rules: { 4 | '@typescript-eslint/require-await': 'off', 5 | }, 6 | } 7 | -------------------------------------------------------------------------------- /apps/www/.gitignore: -------------------------------------------------------------------------------- 1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files. 2 | 3 | # dependencies 4 | /node_modules 5 | /.pnp 6 | .pnp.js 7 | 8 | # testing 9 | /coverage 10 | 11 | # generated 12 | /.next/ 13 | /out/ 14 | /build 15 | generated 16 | public/sitemap.xml 17 | public/robots.txt 18 | 19 | # misc 20 | .DS_Store 21 | *.pem 22 | 23 | # debug 24 | npm-debug.log* 25 | yarn-debug.log* 26 | yarn-error.log* 27 | 28 | # local env files 29 | .env*.local 30 | 31 | # vercel 32 | .vercel 33 | 34 | # typescript 35 | *.tsbuildinfo 36 | next-env.d.ts 37 | 38 | 39 | -------------------------------------------------------------------------------- /apps/www/env.js: -------------------------------------------------------------------------------- 1 | /* eslint-disable @typescript-eslint/no-unsafe-call */ 2 | /* eslint-disable @typescript-eslint/no-unsafe-assignment */ 3 | import { createEnv } from '@t3-oss/env-nextjs' 4 | import { z } from 'zod' 5 | 6 | export const env = createEnv({ 7 | server: {}, 8 | client: { 9 | NEXT_PUBLIC_GA_MEASUREMENT_ID: z.string().optional().default(''), 10 | NEXT_PUBLIC_ALGOLIA_APP_ID: z.string().optional().default(''), 11 | NEXT_PUBLIC_ALGOLIA_SEARCH_KEY: z.string().optional().default(''), 12 | NEXT_PUBLIC_UMAMI_WEBSITE_ID: z.string().optional().default(''), 13 | }, 14 | runtimeEnv: { 15 | NEXT_PUBLIC_GA_MEASUREMENT_ID: process.env.NEXT_PUBLIC_GA_MEASUREMENT_ID, 16 | NEXT_PUBLIC_ALGOLIA_APP_ID: process.env.NEXT_PUBLIC_ALGOLIA_APP_ID, 17 | NEXT_PUBLIC_ALGOLIA_SEARCH_KEY: process.env.NEXT_PUBLIC_ALGOLIA_SEARCH_KEY, 18 | NEXT_PUBLIC_UMAMI_WEBSITE_ID: process.env.NEXT_PUBLIC_UMAMI_WEBSITE_ID, 19 | }, 20 | }) 21 | -------------------------------------------------------------------------------- /apps/www/next-sitemap.config.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Generate sitemap.xml and robots.txt for all kind of pages 3 | * @doc https://www.npmjs.com/package/next-sitemap 4 | */ 5 | 6 | /** @type {import('next-sitemap').IConfig} */ 7 | const config = { 8 | siteUrl: 'https://usehooks-ts.com', 9 | changefreq: 'daily', 10 | priority: 0.7, 11 | generateIndexSitemap: false, 12 | generateRobotsTxt: true, 13 | robotsTxtOptions: { 14 | policies: [ 15 | { 16 | userAgent: '*', 17 | allow: '/', 18 | }, 19 | ], 20 | }, 21 | } 22 | 23 | export default config 24 | -------------------------------------------------------------------------------- /apps/www/next.config.js: -------------------------------------------------------------------------------- 1 | import './env.js' 2 | 3 | const deletedHooks = [ 4 | 'use-debounce', 5 | 'use-effect-once', 6 | 'use-element-size', 7 | 'use-fetch', 8 | 'use-image-on-load', 9 | 'use-is-first-render', 10 | 'use-locked-body', 11 | 'use-update-effect', 12 | ] 13 | 14 | /** @type {import('next').NextConfig} */ 15 | const nextConfig = { 16 | async redirects() { 17 | return deletedHooks.map(slug => ({ 18 | source: `/react-hook/${slug}`, 19 | destination: '/migrate-to-v3#removed-hooks', 20 | permanent: true, 21 | })) 22 | }, 23 | } 24 | 25 | export default nextConfig 26 | -------------------------------------------------------------------------------- /apps/www/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "www", 3 | "version": "1.0.4", 4 | "private": true, 5 | "type": "module", 6 | "scripts": { 7 | "dev": "next dev", 8 | "build": "pnpm generate-doc && next build && pnpm generate-sitemap", 9 | "generate-sitemap": "rimraf public/sitemap.xml public/robots.txt && next-sitemap --config ./next-sitemap.config.js", 10 | "start": "next start", 11 | "lint": "next lint && tsc --noEmit", 12 | "clean": "rimraf *.tsbuildinfo .next .turbo", 13 | "generate-doc": "cd ../.. && pnpm generate-doc && cd -" 14 | }, 15 | "dependencies": { 16 | "@next/third-parties": "^14.1.0", 17 | "@radix-ui/react-dialog": "^1.0.5", 18 | "@radix-ui/react-dropdown-menu": "^2.0.6", 19 | "@radix-ui/react-slot": "^1.0.2", 20 | "@t3-oss/env-nextjs": "^0.9.2", 21 | "@types/voca": "^1.4.1", 22 | "algoliasearch": "^4.22.1", 23 | "class-variance-authority": "^0.7.0", 24 | "clsx": "^2.1.0", 25 | "cmdk": "^1.0.0", 26 | "date-fns": "^3.3.1", 27 | "gray-matter": "^4.0.3", 28 | "lucide-react": "^0.364.0", 29 | "next": "14.1.4", 30 | "next-mdx-remote": "^4.4.1", 31 | "react": "18.2.0", 32 | "react-dom": "18.2.0", 33 | "react-instantsearch": "^7.6.0", 34 | "rehype-prism-plus": "^2.0.0", 35 | "remark-gfm": "^3.0.1", 36 | "schema-dts": "^1.1.2", 37 | "tailwind-merge": "^2.2.1", 38 | "tailwindcss": "3.4.3", 39 | "tailwindcss-animate": "^1.0.7", 40 | "usehooks-ts": "workspace:*", 41 | "voca": "^1.4.1", 42 | "zod": "^3.22.4" 43 | }, 44 | "devDependencies": { 45 | "@tailwindcss/line-clamp": "^0.4.4", 46 | "@tailwindcss/typography": "^0.5.10", 47 | "@types/node": "20.12.2", 48 | "@types/react": "18.2.73", 49 | "@types/react-dom": "18.2.23", 50 | "autoprefixer": "10.4.19", 51 | "eslint-config-custom": "workspace:*", 52 | "eslint-config-next": "14.1.4", 53 | "next-sitemap": "^4.2.3", 54 | "postcss": "8.4.38", 55 | "typescript": "5.4.3" 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /apps/www/postcss.config.cjs: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | plugins: { 3 | tailwindcss: {}, 4 | autoprefixer: {}, 5 | }, 6 | } 7 | -------------------------------------------------------------------------------- /apps/www/public/android-chrome-192x192.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/juliencrn/usehooks-ts/61949134144d3690fe9f521260a16c779a6d3797/apps/www/public/android-chrome-192x192.png -------------------------------------------------------------------------------- /apps/www/public/android-chrome-512x512.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/juliencrn/usehooks-ts/61949134144d3690fe9f521260a16c779a6d3797/apps/www/public/android-chrome-512x512.png -------------------------------------------------------------------------------- /apps/www/public/apple-touch-icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/juliencrn/usehooks-ts/61949134144d3690fe9f521260a16c779a6d3797/apps/www/public/apple-touch-icon.png -------------------------------------------------------------------------------- /apps/www/public/favicon-16x16.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/juliencrn/usehooks-ts/61949134144d3690fe9f521260a16c779a6d3797/apps/www/public/favicon-16x16.png -------------------------------------------------------------------------------- /apps/www/public/favicon-32x32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/juliencrn/usehooks-ts/61949134144d3690fe9f521260a16c779a6d3797/apps/www/public/favicon-32x32.png -------------------------------------------------------------------------------- /apps/www/public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/juliencrn/usehooks-ts/61949134144d3690fe9f521260a16c779a6d3797/apps/www/public/favicon.ico -------------------------------------------------------------------------------- /apps/www/public/site.webmanifest: -------------------------------------------------------------------------------- 1 | {"name":"","short_name":"","icons":[{"src":"/android-chrome-192x192.png","sizes":"192x192","type":"image/png"},{"src":"/android-chrome-512x512.png","sizes":"512x512","type":"image/png"}],"theme_color":"#ffffff","background_color":"#ffffff","display":"standalone"} -------------------------------------------------------------------------------- /apps/www/src/app/(docs)/introduction/page.tsx: -------------------------------------------------------------------------------- 1 | import { CommandCopy } from '@/components/command-copy' 2 | import { PageHeader } from '@/components/docs/page-header' 3 | import { RightSidebar } from '@/components/docs/right-sidebar' 4 | import { components } from '@/components/ui/components' 5 | import { siteConfig } from '@/config/site' 6 | 7 | export default async function IntroductionPage() { 8 | return ( 9 |
10 |
11 | 16 | Introduction 17 | 18 | useHooks(🔥).ts 19 | is a React hooks library, written in Typescript and easy to use. It 20 | provides a set of hooks that enables you to build your React 21 | applications faster. The hooks are built upon the principles of{' '} 22 | DRY (Don't Repeat 23 | Yourself). There are hooks for most common use cases you might need. 24 | 25 | 26 | The library is designed to be as minimal as possible. It is fully 27 | tree-shakable (using the ESM version), meaning that you only import 28 | the hooks you need, and the rest will be removed from your bundle 29 | making the cost of using this library negligible. Most hooks are 30 | extensively tested and are being used in production environments. 31 | 32 | Install 33 | 34 | Get started installing{' '} 35 | 36 | usehooks-ts 37 | {' '} 38 | using your preferred package manager. 39 | 40 | 49 |
50 | 51 | 59 |
60 | ) 61 | } 62 | -------------------------------------------------------------------------------- /apps/www/src/app/(docs)/layout.tsx: -------------------------------------------------------------------------------- 1 | import Link from 'next/link' 2 | 3 | import { DocSearch } from '@/components/doc-search' 4 | import { LeftSidebar } from '@/components/docs/left-sidebar' 5 | import { MainNav } from '@/components/main-nav' 6 | import { GitHub } from '@/components/ui/icons' 7 | import { docsConfig } from '@/config/docs' 8 | import { siteConfig } from '@/config/site' 9 | import { getHookList } from '@/lib/api' 10 | 11 | type DocsLayoutProps = { 12 | children: React.ReactNode 13 | } 14 | 15 | export default async function DocsLayout({ children }: DocsLayoutProps) { 16 | const hooks = await getHookList() 17 | 18 | return ( 19 | <> 20 |
21 |
22 | 23 | 24 | 25 |
26 | 38 |
39 |
40 |
41 | 42 |
43 |
44 | 45 | {children} 46 |
47 |
48 | 49 | ) 50 | } 51 | -------------------------------------------------------------------------------- /apps/www/src/app/(marketing)/layout.tsx: -------------------------------------------------------------------------------- 1 | import Link from 'next/link' 2 | 3 | import { DocSearch } from '@/components/doc-search' 4 | import { MainNav } from '@/components/main-nav' 5 | import { GitHub } from '@/components/ui/icons' 6 | import { marketingConfig } from '@/config/marketing' 7 | import { siteConfig } from '@/config/site' 8 | 9 | type MarketingLayoutProps = { 10 | children: React.ReactNode 11 | } 12 | 13 | export default async function MarketingLayout({ 14 | children, 15 | }: MarketingLayoutProps) { 16 | return ( 17 | <> 18 |
19 |
20 | 21 | 34 |
35 |
36 | 37 |
{children}
38 | 39 | ) 40 | } 41 | -------------------------------------------------------------------------------- /apps/www/src/app/globals.css: -------------------------------------------------------------------------------- 1 | @tailwind base; 2 | @tailwind components; 3 | @tailwind utilities; 4 | @import './prism.css'; 5 | 6 | @layer base { 7 | :root { 8 | --background: 0 0% 100%; 9 | --foreground: 222.2 47.4% 11.2%; 10 | 11 | --muted: 210 40% 96.1%; 12 | --muted-foreground: 215.4 16.3% 46.9%; 13 | 14 | --popover: 0 0% 100%; 15 | --popover-foreground: 222.2 47.4% 11.2%; 16 | 17 | --card: 0 0% 100%; 18 | --card-foreground: 222.2 47.4% 11.2%; 19 | 20 | --border: 214.3 31.8% 91.4%; 21 | --input: 214.3 31.8% 91.4%; 22 | 23 | --primary: 222.2 47.4% 11.2%; 24 | --primary-foreground: 210 40% 98%; 25 | 26 | --secondary: 210 40% 96.1%; 27 | --secondary-foreground: 222.2 47.4% 11.2%; 28 | 29 | --accent: 210 40% 96.1%; 30 | --accent-foreground: 222.2 47.4% 11.2%; 31 | 32 | --destructive: 0 100% 50%; 33 | --destructive-foreground: 210 40% 98%; 34 | 35 | --ring: 215 20.2% 65.1%; 36 | 37 | --radius: 0.5rem; 38 | } 39 | 40 | @media (prefers-color-scheme: dark) { 41 | :root { 42 | --background: 224 71% 4%; 43 | --foreground: 213 31% 91%; 44 | 45 | --muted: 223 47% 11%; 46 | --muted-foreground: 215.4 16.3% 56.9%; 47 | 48 | --popover: 224 71% 4%; 49 | --popover-foreground: 215 20.2% 65.1%; 50 | 51 | --card: 224 71% 4%; 52 | --card-foreground: 213 31% 91%; 53 | 54 | --border: 216 34% 17%; 55 | --input: 216 34% 17%; 56 | 57 | --primary: 210 40% 98%; 58 | --primary-foreground: 222.2 47.4% 1.2%; 59 | 60 | --secondary: 222.2 47.4% 11.2%; 61 | --secondary-foreground: 210 40% 98%; 62 | 63 | --accent: 216 34% 17%; 64 | --accent-foreground: 210 40% 98%; 65 | 66 | --destructive: 0 63% 31%; 67 | --destructive-foreground: 210 40% 98%; 68 | 69 | --ring: 216 34% 17%; 70 | 71 | --radius: 0.5rem; 72 | } 73 | } 74 | } 75 | 76 | @layer base { 77 | * { 78 | @apply border-border; 79 | } 80 | html { 81 | scroll-behavior: smooth; 82 | 83 | color-scheme: light; 84 | 85 | @media (prefers-color-scheme: dark) { 86 | color-scheme: dark; 87 | } 88 | } 89 | body { 90 | @apply bg-background text-foreground; 91 | font-feature-settings: 92 | 'rlig' 1, 93 | 'calt' 1; 94 | } 95 | 96 | /* Override the default styles for the Carbon Ads block */ 97 | .carbon-wrap * { 98 | --carbon-bg-primary: hsl(var(--background)); 99 | --carbon-bg-secondary: hsl(var(--border)); 100 | --carbon-text-color: hsl(var(--foreground)); 101 | } 102 | 103 | .carbon-wrap .carbon-responsive-wrap { 104 | @apply !rounded-md; 105 | } 106 | 107 | .carbon-wrap .carbon-poweredby { 108 | @apply !opacity-100 !text-muted-foreground; 109 | } 110 | } 111 | -------------------------------------------------------------------------------- /apps/www/src/assets/fonts/CalSans-SemiBold.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/juliencrn/usehooks-ts/61949134144d3690fe9f521260a16c779a6d3797/apps/www/src/assets/fonts/CalSans-SemiBold.ttf -------------------------------------------------------------------------------- /apps/www/src/assets/fonts/CalSans-SemiBold.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/juliencrn/usehooks-ts/61949134144d3690fe9f521260a16c779a6d3797/apps/www/src/assets/fonts/CalSans-SemiBold.woff -------------------------------------------------------------------------------- /apps/www/src/assets/fonts/CalSans-SemiBold.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/juliencrn/usehooks-ts/61949134144d3690fe9f521260a16c779a6d3797/apps/www/src/assets/fonts/CalSans-SemiBold.woff2 -------------------------------------------------------------------------------- /apps/www/src/components/carbon-ads/ads.tsx: -------------------------------------------------------------------------------- 1 | import type { ComponentPropsWithoutRef } from 'react' 2 | 3 | import { useScript } from './use-script' 4 | import { cn } from '@/lib/utils' 5 | 6 | const adIds = { 7 | home: 'CWYIE23E', 8 | docs: 'CWYIEKJU', 9 | } as const 10 | 11 | type CarbonAdsProps = { 12 | variant: keyof typeof adIds 13 | /** @default cover */ 14 | format?: 'responsive' | 'cover' 15 | } & ComponentPropsWithoutRef<'div'> 16 | 17 | export function CarbonAds({ 18 | variant, 19 | format = 'cover', 20 | className, 21 | ...props 22 | }: CarbonAdsProps) { 23 | const ref = useScript( 24 | `//cdn.carbonads.com/carbon.js?serve=${adIds[variant]}&placement=usehooks-tscom&format=${format}`, 25 | '_carbonads_js', 26 | ) 27 | 28 | return
29 | } 30 | -------------------------------------------------------------------------------- /apps/www/src/components/carbon-ads/index.ts: -------------------------------------------------------------------------------- 1 | 'use client' 2 | 3 | export * from './ads' 4 | -------------------------------------------------------------------------------- /apps/www/src/components/carbon-ads/use-script.ts: -------------------------------------------------------------------------------- 1 | import { useEffect, useRef } from 'react' 2 | 3 | // TODO: We can't use usehooks-ts's useScript because it mounts the script in the document.body, maybe provide a way to specify the mount point 4 | export const useScript = (scriptUrl: string, scriptId: string) => { 5 | const ref = useRef(null) 6 | 7 | useEffect(() => { 8 | const existingScript = document.getElementById(scriptId) 9 | 10 | if (!existingScript) { 11 | const script = document.createElement('script') 12 | 13 | script.setAttribute('async', '') 14 | script.setAttribute('type', 'text/javascript') 15 | script.setAttribute('src', scriptUrl) 16 | script.setAttribute('id', scriptId) 17 | 18 | ref.current?.appendChild(script) 19 | } 20 | 21 | return () => { 22 | if (existingScript) { 23 | existingScript.remove() 24 | } 25 | } 26 | }, [scriptUrl, scriptId]) 27 | 28 | return ref 29 | } 30 | -------------------------------------------------------------------------------- /apps/www/src/components/command-copy.tsx: -------------------------------------------------------------------------------- 1 | 'use client' 2 | 3 | import { useState } from 'react' 4 | 5 | import { Check, Copy } from 'lucide-react' 6 | import type { ComponentProps } from 'react' 7 | import { useCopyToClipboard } from 'usehooks-ts' 8 | 9 | import { Button } from './ui/button' 10 | import { 11 | DropdownMenu, 12 | DropdownMenuContent, 13 | DropdownMenuItem, 14 | DropdownMenuTrigger, 15 | } from './ui/dropdown-menu' 16 | import { cn } from '@/lib/utils' 17 | 18 | type CommandCopyProps = { 19 | command: Record | string 20 | defaultCommand?: string 21 | } & ComponentProps<'code'> 22 | 23 | export function CommandCopy({ 24 | className, 25 | command, 26 | defaultCommand, 27 | ...props 28 | }: CommandCopyProps) { 29 | const [copiedStatus, setCopiedStatus] = useState(false) 30 | const [, copy] = useCopyToClipboard() 31 | 32 | const handleCopy = (text: string) => { 33 | setCopiedStatus(true) 34 | void copy(text) 35 | setTimeout(() => { 36 | setCopiedStatus(false) 37 | }, 2000) 38 | } 39 | const renderedCommand = 40 | typeof command === 'string' 41 | ? command 42 | : command[defaultCommand ?? Object.keys(command)[0]] 43 | 44 | return ( 45 | 52 |
53 | {renderedCommand.split(' ').map((arg, i) => ( 54 | 58 | {arg}{' '} 59 | 60 | ))} 61 |
62 | 63 | 64 | 79 | 80 | 81 | {command && 82 | Object.entries(command).map(([key, value]) => ( 83 | { 86 | handleCopy(value) 87 | }} 88 | > 89 | {key} 90 | 91 | ))} 92 | 93 | 94 |
95 | ) 96 | } 97 | -------------------------------------------------------------------------------- /apps/www/src/components/doc-search/command-menu.tsx: -------------------------------------------------------------------------------- 1 | import { Index } from 'react-instantsearch' 2 | 3 | import { Footer } from './footer' 4 | import { RenderHits } from './hits' 5 | import { SearchInput } from './input' 6 | import { useCommandMenuContext } from './modal.context' 7 | import { 8 | CommandDialog, 9 | CommandEmpty, 10 | CommandGroup, 11 | CommandList, 12 | } from '@/components/ui/command' 13 | 14 | export function CommandMenu() { 15 | const { open, setOpen } = useCommandMenuContext() 16 | 17 | return ( 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | No results found. 32 | 33 |