├── .changeset
└── config.json
├── .github
├── ISSUE_TEMPLATE
│ ├── bug_report.md
│ └── feature_request.md
└── workflows
│ ├── codeql-analysis.yml
│ ├── deploy.yml
│ ├── e2e-testing.yml
│ ├── main.yml
│ ├── pull-request.yml
│ └── release.yml
├── .gitignore
├── .npmrc
├── .nvmrc
├── .prettierrc.js
├── CHANGELOG.md
├── CONTRIBUTING.md
├── LICENSE
├── README.md
├── apps
├── examples
│ ├── README.md
│ ├── gatsby
│ │ ├── .gitignore
│ │ ├── README.md
│ │ ├── gatsby-browser.js
│ │ ├── gatsby-config.js
│ │ ├── gatsby-ssr.js
│ │ ├── package.json
│ │ ├── src
│ │ │ ├── images
│ │ │ │ └── icon.png
│ │ │ └── pages
│ │ │ │ ├── 404.js
│ │ │ │ └── index.js
│ │ └── wrap-with-provider.jsx
│ ├── nextjs-app-router
│ │ ├── .gitignore
│ │ ├── README.md
│ │ ├── app
│ │ │ ├── intercom.jsx
│ │ │ ├── layout.jsx
│ │ │ └── page.jsx
│ │ └── package.json
│ ├── nextjs-page-router
│ │ ├── .gitignore
│ │ ├── README.md
│ │ ├── package.json
│ │ ├── pages
│ │ │ ├── _app.js
│ │ │ ├── api
│ │ │ │ └── hello.js
│ │ │ └── index.js
│ │ ├── public
│ │ │ ├── favicon.ico
│ │ │ └── vercel.svg
│ │ └── styles
│ │ │ ├── Home.module.css
│ │ │ └── globals.css
│ └── vite
│ │ ├── .gitignore
│ │ ├── README.md
│ │ ├── eslint.config.js
│ │ ├── index.html
│ │ ├── package.json
│ │ ├── src
│ │ ├── app.jsx
│ │ └── main.jsx
│ │ └── vite.config.js
└── playground
│ ├── .gitignore
│ ├── assets
│ └── images
│ │ ├── githubLogo.svg
│ │ └── logo.png
│ ├── cypress.config.ts
│ ├── cypress
│ ├── e2e
│ │ ├── boot.ts
│ │ ├── hide.ts
│ │ ├── initializeDelay.ts
│ │ ├── mappers.ts
│ │ ├── provider.ts
│ │ ├── show.ts
│ │ ├── shutdown.ts
│ │ ├── startSurvey.ts
│ │ ├── update.ts
│ │ └── visitorId.ts
│ ├── support
│ │ └── e2e.ts
│ └── tsconfig.json
│ ├── favicon.ico
│ ├── global.d.ts
│ ├── index.html
│ ├── index.tsx
│ ├── package.json
│ ├── src
│ ├── app.tsx
│ ├── modules
│ │ ├── common
│ │ │ ├── button.tsx
│ │ │ ├── index.ts
│ │ │ ├── page.tsx
│ │ │ └── style.tsx
│ │ ├── index.ts
│ │ ├── provider
│ │ │ ├── index.ts
│ │ │ ├── provider.tsx
│ │ │ ├── providerApi.tsx
│ │ │ ├── providerAutoBootProps.tsx
│ │ │ └── providerEvents.tsx
│ │ └── useIntercom
│ │ │ ├── index.ts
│ │ │ ├── useIntercom.tsx
│ │ │ ├── useIntercomTour.tsx
│ │ │ └── useIntercomWithDelay.tsx
│ └── vite-env.d.ts
│ ├── tsconfig.json
│ └── vite.config.ts
├── assets
└── logo.png
├── eslint.config.js
├── package.json
├── packages
└── react-use-intercom
│ ├── .gitignore
│ ├── .npmignore
│ ├── CHANGELOG.md
│ ├── README.md
│ ├── jest.config.cjs
│ ├── package.json
│ ├── src
│ ├── api.ts
│ ├── context.ts
│ ├── global.d.ts
│ ├── index.ts
│ ├── initialize.ts
│ ├── logger.ts
│ ├── mappers.ts
│ ├── provider.tsx
│ ├── types.ts
│ ├── useIntercom.ts
│ └── utils.ts
│ ├── test
│ ├── config.ts
│ ├── intercomProvider.test.tsx
│ ├── test-env.d.ts
│ └── useIntercom.test.tsx
│ ├── tsconfig.json
│ └── tsup.config.ts
├── pnpm-lock.yaml
├── pnpm-workspace.yaml
└── turbo.json
/.changeset/config.json:
--------------------------------------------------------------------------------
1 | {
2 | "$schema": "https://unpkg.com/@changesets/config@2.0.0/schema.json",
3 | "changelog": "@changesets/cli/changelog",
4 | "commit": false,
5 | "ignore": ["playground", "*-example"],
6 | "access": "public",
7 | "updateInternalDependencies": "patch",
8 | "baseBranch": "main",
9 | "bumpVersionsWithWorkspaceProtocolOnly": true
10 | }
11 |
--------------------------------------------------------------------------------
/.github/ISSUE_TEMPLATE/bug_report.md:
--------------------------------------------------------------------------------
1 | ---
2 | name: Bug report
3 | about: Create a report to help us improve
4 | title: ''
5 | labels: ''
6 | assignees: devrnt
7 |
8 | ---
9 |
10 | **Describe the bug**
11 | A clear and concise description of what the bug is.
12 |
13 | **To Reproduce**
14 | Steps to reproduce the behavior:
15 | 1. Go to '...'
16 | 2. Click on '....'
17 | 3. Scroll down to '....'
18 | 4. See error
19 |
20 | **StackBlitz link**
21 | Include a StackBLitz will help us to investigate the issue quicker.
22 |
23 | [](https://stackblitz.com/fork/github/devrnt/react-use-intercom/)
24 |
25 | **Expected behavior**
26 | A clear and concise description of what you expected to happen.
27 |
28 | **Screenshots**
29 | If applicable, add screenshots to help explain your problem.
30 |
31 | **Desktop (please complete the following information):**
32 | - OS: [e.g. iOS]
33 | - Browser [e.g. chrome, safari]
34 | - Version [e.g. 22]
35 |
36 | **Smartphone (please complete the following information):**
37 | - Device: [e.g. iPhone6]
38 | - OS: [e.g. iOS8.1]
39 | - Browser [e.g. stock browser, safari]
40 | - Version [e.g. 22]
41 |
42 | **Additional context**
43 | Add any other context about the problem here.
44 |
--------------------------------------------------------------------------------
/.github/ISSUE_TEMPLATE/feature_request.md:
--------------------------------------------------------------------------------
1 | ---
2 | name: Feature request
3 | about: Suggest an idea for this project
4 | title: ''
5 | labels: ''
6 | assignees: devrnt
7 |
8 | ---
9 |
10 | **Is your feature request related to a problem? Please describe.**
11 | A clear and concise description of what the problem is. Ex. I'm always frustrated when [...]
12 |
13 | **Describe the solution you'd like**
14 | A clear and concise description of what you want to happen.
15 |
16 | **Describe alternatives you've considered**
17 | A clear and concise description of any alternative solutions or features you've considered.
18 |
19 | **Additional context**
20 | Add any other context or screenshots about the feature request here.
21 |
--------------------------------------------------------------------------------
/.github/workflows/codeql-analysis.yml:
--------------------------------------------------------------------------------
1 | # For most projects, this workflow file will not need changing; you simply need
2 | # to commit it to your repository.
3 | #
4 | # You may wish to alter this file to override the set of languages analyzed,
5 | # or to provide custom queries or build logic.
6 | #
7 | # ******** NOTE ********
8 | # We have attempted to detect the languages in your repository. Please check
9 | # the `language` matrix defined below to confirm you have the correct set of
10 | # supported CodeQL languages.
11 | #
12 | name: "CodeQL"
13 |
14 | on:
15 | push:
16 | branches: [ main ]
17 | pull_request:
18 | # The branches below must be a subset of the branches above
19 | branches: [ main ]
20 | schedule:
21 | - cron: '45 18 * * 3'
22 |
23 | jobs:
24 | analyze:
25 | name: Analyze
26 | runs-on: ubuntu-24.04
27 |
28 | strategy:
29 | fail-fast: false
30 | matrix:
31 | language: [ 'javascript' ]
32 | # CodeQL supports [ 'cpp', 'csharp', 'go', 'java', 'javascript', 'python' ]
33 | # Learn more:
34 | # https://docs.github.com/en/free-pro-team@latest/github/finding-security-vulnerabilities-and-errors-in-your-code/configuring-code-scanning#changing-the-languages-that-are-analyzed
35 |
36 | steps:
37 | - name: Checkout
38 | uses: actions/checkout@v3
39 |
40 | # Initializes the CodeQL tools for scanning.
41 | - name: Initialize CodeQL
42 | uses: github/codeql-action/init@v1
43 | with:
44 | languages: ${{ matrix.language }}
45 | # If you wish to specify custom queries, you can do so here or in a config file.
46 | # By default, queries listed here will override any specified in a config file.
47 | # Prefix the list here with "+" to use these queries and those in the config file.
48 | # queries: ./path/to/local/query, your-org/your-repo/queries@main
49 |
50 | # Autobuild attempts to build any compiled languages (C/C++, C#, or Java).
51 | # If this step fails, then you should remove it and run the build manually (see below)
52 | - name: Autobuild
53 | uses: github/codeql-action/autobuild@v1
54 |
55 | # ℹ️ Command-line programs to run using the OS shell.
56 | # 📚 https://git.io/JvXDl
57 |
58 | # ✏️ If the Autobuild fails above, remove it and uncomment the following three lines
59 | # and modify them (or add more) to build your code if your project
60 | # uses a compiled language
61 |
62 | #- run: |
63 | # make bootstrap
64 | # make release
65 |
66 | - name: Perform CodeQL Analysis
67 | uses: github/codeql-action/analyze@v1
68 |
--------------------------------------------------------------------------------
/.github/workflows/deploy.yml:
--------------------------------------------------------------------------------
1 | name: Deploy to Github Pages
2 |
3 | on:
4 | push:
5 | branches:
6 | - main
7 |
8 | jobs:
9 | deploy:
10 | runs-on: ubuntu-24.04
11 | strategy:
12 | matrix:
13 | node-version: [22.13.1]
14 | steps:
15 | - name: Checkout
16 | uses: actions/checkout@v4
17 |
18 | - name: Install pnpm
19 | uses: pnpm/action-setup@v3
20 |
21 | - name: Use Node ${{ matrix.node-version }}
22 | uses: actions/setup-node@v4
23 | with:
24 | node-version: ${{ matrix.node-version }}
25 | cache: 'pnpm'
26 |
27 | - name: Install dependencies
28 | run: pnpm install --frozen-lockfile
29 |
30 | - name: Build playground
31 | run: pnpm build:playground
32 |
33 | - name: Deploy
34 | uses: peaceiris/actions-gh-pages@v3
35 | with:
36 | github_token: ${{ secrets.GITHUB_TOKEN }}
37 | publish_dir: ./apps/playground/dist
38 |
--------------------------------------------------------------------------------
/.github/workflows/e2e-testing.yml:
--------------------------------------------------------------------------------
1 | name: End-to-end tests
2 | on: [pull_request]
3 | jobs:
4 | cypress-run:
5 | runs-on: ubuntu-24.04
6 | strategy:
7 | matrix:
8 | node-version: [22.13.1]
9 |
10 | steps:
11 | - name: Checkout
12 | uses: actions/checkout@v4
13 |
14 | - name: Install pnpm
15 | uses: pnpm/action-setup@v3
16 |
17 | - name: Use Node ${{ matrix.node-version }}
18 | uses: actions/setup-node@v4
19 | with:
20 | node-version: ${{ matrix.node-version }}
21 | cache: 'pnpm'
22 |
23 | - name: Install dependencies
24 | run: pnpm install --frozen-lockfile
25 |
26 | - name: Build
27 | run: pnpm build
28 |
29 | - name: Cypress install
30 | run: ./node_modules/cypress/bin/cypress install
31 |
32 | - name: Cypress run
33 | uses: cypress-io/github-action@v6
34 | with:
35 | install: false
36 | start: pnpm dev
37 | command: pnpm e2e
38 | wait-on: 'http://localhost:5173'
39 | project: ./apps/playground
40 |
41 | # after the test run completes
42 | # store videos and any screenshots
43 | # NOTE: screenshots will be generated only if E2E test failed
44 | # thus we store screenshots only on failures
45 | # Alternative: create and commit an empty cypress/screenshots folder
46 | # to always have something to upload
47 | - name: Upload Cypress artifacts
48 | uses: actions/upload-artifact@v4
49 | if: failure()
50 | with:
51 | name: cypress-screenshots
52 | path: ./apps/playground/cypress/screenshots
53 |
--------------------------------------------------------------------------------
/.github/workflows/main.yml:
--------------------------------------------------------------------------------
1 | name: CI
2 | on: [push, pull_request]
3 | jobs:
4 | build:
5 | runs-on: ubuntu-24.04
6 | strategy:
7 | matrix:
8 | node-version: [22.13.1]
9 | steps:
10 | - name: Checkout
11 | uses: actions/checkout@v4
12 |
13 | - name: Install pnpm
14 | uses: pnpm/action-setup@v3
15 |
16 | - name: Use Node ${{ matrix.node-version }}
17 | uses: actions/setup-node@v4
18 | with:
19 | node-version: ${{ matrix.node-version }}
20 | cache: 'pnpm'
21 |
22 | - name: Install dependencies
23 | run: pnpm install --frozen-lockfile
24 |
25 | - name: Lint
26 | run: pnpm lint
27 |
28 | - name: Test
29 | run: pnpm test
30 |
31 | - name: Build
32 | run: pnpm build
33 |
--------------------------------------------------------------------------------
/.github/workflows/pull-request.yml:
--------------------------------------------------------------------------------
1 | name: Compressed Size
2 | on: [pull_request]
3 | jobs:
4 | build:
5 | runs-on: ubuntu-24.04
6 | strategy:
7 | matrix:
8 | node-version: [22.13.1]
9 |
10 | steps:
11 | - name: Checkout
12 | uses: actions/checkout@v4
13 |
14 | - name: Install pnpm
15 | uses: pnpm/action-setup@v3
16 |
17 | - name: Use Node ${{ matrix.node-version }}
18 | uses: actions/setup-node@v4
19 | with:
20 | node-version: ${{ matrix.node-version }}
21 | cache: 'pnpm'
22 |
23 | - name: Install dependencies
24 | run: pnpm install --frozen-lockfile
25 |
26 | - name: Build
27 | run: pnpm build
28 |
29 | - uses: preactjs/compressed-size-action@v2
30 | with:
31 | repo-token: '${{ secrets.GITHUB_TOKEN }}'
32 | pattern: "packages/**/dist/**/*.?(c)js"
33 |
--------------------------------------------------------------------------------
/.github/workflows/release.yml:
--------------------------------------------------------------------------------
1 | name: Release
2 |
3 | on:
4 | push:
5 | branches:
6 | - main
7 |
8 | concurrency: ${{ github.workflow }}-${{ github.ref }}
9 |
10 | jobs:
11 | release:
12 | name: Release
13 | runs-on: ubuntu-24.04
14 | strategy:
15 | matrix:
16 | node-version: [22.13.1]
17 |
18 | steps:
19 | - name: Checkout
20 | uses: actions/checkout@v4
21 |
22 | - name: Install pnpm
23 | uses: pnpm/action-setup@v3
24 |
25 | - name: Use Node ${{ matrix.node-version }}
26 | uses: actions/setup-node@v4
27 | with:
28 | node-version: ${{ matrix.node-version }}
29 | cache: 'pnpm'
30 |
31 | - name: Install dependencies
32 | run: pnpm install --frozen-lockfile
33 |
34 | - name: Create Release Pull Request or Publish to npm
35 | id: changesets
36 | uses: changesets/action@v1
37 | with:
38 | # This expects you to have a script called release which does a build for your packages and calls changeset publish
39 | publish: pnpm release
40 | env:
41 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
42 | NPM_TOKEN: ${{ secrets.NPM_TOKEN }}
43 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | *.log
2 | .DS_Store
3 | node_modules
4 | .cache
5 | dist
6 |
7 | .turbo
8 |
--------------------------------------------------------------------------------
/.npmrc:
--------------------------------------------------------------------------------
1 | auto-install-peers = true
2 | node-linker=hoisted
3 | # This allows us to use workspace packages if available, otherwise pull from the remote registry
4 | link-workspace-packages = true
--------------------------------------------------------------------------------
/.nvmrc:
--------------------------------------------------------------------------------
1 | 22.13.1
--------------------------------------------------------------------------------
/.prettierrc.js:
--------------------------------------------------------------------------------
1 | export default {
2 | printWidth: 80,
3 | semi: true,
4 | singleQuote: true,
5 | trailingComma: 'all',
6 | arrowParens: 'always',
7 | };
8 |
--------------------------------------------------------------------------------
/CHANGELOG.md:
--------------------------------------------------------------------------------
1 | # Changelog
2 |
3 | See [releases](https://github.com/devrnt/react-use-intercom/releases)
--------------------------------------------------------------------------------
/CONTRIBUTING.md:
--------------------------------------------------------------------------------
1 | # Contributing to react-use-intercom
2 |
3 | ## Preparing
4 |
5 | 1. Install pnpm:
6 |
7 | ```sh
8 | npm install -g pnpm@10.2
9 | ```
10 |
11 | 2. Fork and clone the repository
12 | 3. Install dependencies:
13 |
14 | ```sh
15 | pnpm install
16 | ```
17 |
18 | 4. The project uses a monorepo structure with pnpm workspaces:
19 |
20 | - `/packages/react-use-intercom`: Main library package
21 | - `/apps/playground`: Development playground
22 | - `/apps/examples`: Example applications
23 |
24 | ## Development Workflow
25 |
26 | 1. Create a new branch for your feature/fix:
27 |
28 | ```sh
29 | git checkout -b your-feature-name
30 | ```
31 |
32 | 2. Make your changes in the appropriate package
33 |
34 | 3. To test your changes:
35 |
36 | - Start the playground: `pnpm dev`
37 | - Run the test suite: `pnpm test`
38 | - Run E2E tests: `pnpm test:e2e`
39 |
40 | 4. Create a changeset to document your changes:
41 |
42 | ```sh
43 | pnpm changeset
44 | ```
45 |
46 | Follow the prompts to describe your changes. This step is required for any user-facing changes.
47 |
48 | 5. Before submitting your PR:
49 | - Ensure all tests pass
50 | - Add or update tests as needed
51 | - Follow the existing code style
52 | - Update documentation if necessary
53 | - Verify your changeset accurately describes the changes
54 |
55 | ## Pull Request
56 |
57 | 1. Update the README.md with details of changes if applicable
58 | 2. Ensure your PR includes a changeset if it includes user-facing changes
59 |
60 | ## Testing
61 |
62 | - Write unit tests for new features
63 | - Update existing tests when modifying features
64 | - Ensure all tests pass before submitting a PR
65 | - Add E2E tests for new user-facing features
66 |
67 | ## Documentation
68 |
69 | - Update the README.md if you add or modify features
70 | - Include JSDoc comments for public APIs
71 | - Keep documentation clear and concise
72 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2020 devrnt
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.
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | ./packages/react-use-intercom/README.md
--------------------------------------------------------------------------------
/apps/examples/README.md:
--------------------------------------------------------------------------------
1 | # Examples
2 |
3 | * [gatsby](https://stackblitz.com/github/devrnt/react-use-intercom/tree/main/apps/examples/gatsby)
4 | * [nextjs-page-router](https://stackblitz.com/github/devrnt/react-use-intercom/tree/main/apps/examples/nextjs-page-router)
5 | * [nextjs-app-router](https://stackblitz.com/github/devrnt/react-use-intercom/tree/main/apps/examples/nextjs-app-router)
--------------------------------------------------------------------------------
/apps/examples/gatsby/.gitignore:
--------------------------------------------------------------------------------
1 | node_modules/
2 | .cache/
3 | public
4 |
--------------------------------------------------------------------------------
/apps/examples/gatsby/README.md:
--------------------------------------------------------------------------------
1 | # react-use-intercom in Gatsby
2 |
3 | Replace `INTERCOM_APP_ID` with your Intercom app id.
4 |
--------------------------------------------------------------------------------
/apps/examples/gatsby/gatsby-browser.js:
--------------------------------------------------------------------------------
1 | import wrapWithProvider from './wrap-with-provider';
2 |
3 | export const wrapRootElement = wrapWithProvider;
4 |
--------------------------------------------------------------------------------
/apps/examples/gatsby/gatsby-config.js:
--------------------------------------------------------------------------------
1 | /**
2 | * @type {import('gatsby').GatsbyConfig}
3 | */
4 | module.exports = {
5 | siteMetadata: {
6 | siteUrl: `https://www.yourdomain.tld`,
7 | },
8 | plugins: [],
9 | };
10 |
--------------------------------------------------------------------------------
/apps/examples/gatsby/gatsby-ssr.js:
--------------------------------------------------------------------------------
1 | import wrapWithProvider from './wrap-with-provider';
2 |
3 | export const wrapRootElement = wrapWithProvider;
4 |
--------------------------------------------------------------------------------
/apps/examples/gatsby/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "gatsby-example",
3 | "private": true,
4 | "description": "My Gatsby Site",
5 | "author": "devrnt",
6 | "keywords": [
7 | "gatsby"
8 | ],
9 | "scripts": {
10 | "dev": "gatsby develop",
11 | "build": "gatsby build",
12 | "serve": "gatsby serve",
13 | "clean": "gatsby clean"
14 | },
15 | "dependencies": {
16 | "gatsby": "^5.5.0",
17 | "react": "^18.2.0",
18 | "react-dom": "^18.2.0",
19 | "react-use-intercom": "*"
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/apps/examples/gatsby/src/images/icon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/devrnt/react-use-intercom/a89e1734584ac8421d8d81681be48e378786116f/apps/examples/gatsby/src/images/icon.png
--------------------------------------------------------------------------------
/apps/examples/gatsby/src/pages/404.js:
--------------------------------------------------------------------------------
1 | import * as React from "react"
2 | import { Link } from "gatsby"
3 |
4 | const pageStyles = {
5 | color: "#232129",
6 | padding: "96px",
7 | fontFamily: "-apple-system, Roboto, sans-serif, serif",
8 | }
9 | const headingStyles = {
10 | marginTop: 0,
11 | marginBottom: 64,
12 | maxWidth: 320,
13 | }
14 |
15 | const paragraphStyles = {
16 | marginBottom: 48,
17 | }
18 | const codeStyles = {
19 | color: "#8A6534",
20 | padding: 4,
21 | backgroundColor: "#FFF4DB",
22 | fontSize: "1.25rem",
23 | borderRadius: 4,
24 | }
25 |
26 | const NotFoundPage = () => {
27 | return (
28 |
29 | Page not found
30 |
31 | Sorry 😔, we couldn’t find what you were looking for.
32 |
33 | {process.env.NODE_ENV === "development" ? (
34 | <>
35 |
36 | Try creating a page in src/pages/
.
37 |
38 | >
39 | ) : null}
40 |
41 | Go home.
42 |
43 |
44 | )
45 | }
46 |
47 | export default NotFoundPage
48 |
49 | export const Head = () =>
Not found
50 |
--------------------------------------------------------------------------------
/apps/examples/gatsby/src/pages/index.js:
--------------------------------------------------------------------------------
1 | import * as React from 'react';
2 | import { useIntercom } from 'react-use-intercom';
3 |
4 | const IndexPage = () => {
5 | const { boot } = useIntercom();
6 |
7 | return (
8 |
16 | boot()}>Boot
17 |
18 | );
19 | };
20 |
21 | export default IndexPage;
22 |
--------------------------------------------------------------------------------
/apps/examples/gatsby/wrap-with-provider.jsx:
--------------------------------------------------------------------------------
1 | import * as React from 'react';
2 | import { IntercomProvider } from 'react-use-intercom';
3 |
4 | const INTERCOM_APP_ID = 'jcabc7e3';
5 |
6 | export default ({ element }) => {
7 | // Instantiating store in `wrapRootElement` handler ensures:
8 | // - there is fresh store for each SSR page
9 | // - it will be called only once in browser, when React mounts
10 | return {element} ;
11 | };
12 |
--------------------------------------------------------------------------------
/apps/examples/nextjs-app-router/.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 | # next.js
12 | /.next/
13 | /out/
14 |
15 | # production
16 | /build
17 |
18 | # misc
19 | .DS_Store
20 | *.pem
21 |
22 | # debug
23 | npm-debug.log*
24 | yarn-debug.log*
25 | yarn-error.log*
26 |
27 | # local env files
28 | .env.local
29 | .env.development.local
30 | .env.test.local
31 | .env.production.local
32 |
33 | # vercel
34 | .vercel
35 |
--------------------------------------------------------------------------------
/apps/examples/nextjs-app-router/README.md:
--------------------------------------------------------------------------------
1 | # react-use-intercom in NextJS (App Router)
2 |
3 | Replace `INTERCOM_APP_ID` with your Intercom app id.
--------------------------------------------------------------------------------
/apps/examples/nextjs-app-router/app/intercom.jsx:
--------------------------------------------------------------------------------
1 | 'use client';
2 |
3 | import { IntercomProvider } from 'react-use-intercom';
4 |
5 | const INTERCOM_APP_ID = 'jcabc7e3';
6 |
7 | export function OurIntercomProvider({ children }) {
8 | return (
9 |
10 |
11 |
12 | {children}
13 |
14 |
15 |
16 | );
17 | }
18 |
--------------------------------------------------------------------------------
/apps/examples/nextjs-app-router/app/layout.jsx:
--------------------------------------------------------------------------------
1 | import { OurIntercomProvider } from './intercom';
2 |
3 | export default function RootLayout({ children }) {
4 | return (
5 |
6 |
7 |
8 | {children}
9 |
10 |
11 |
12 | );
13 | }
--------------------------------------------------------------------------------
/apps/examples/nextjs-app-router/app/page.jsx:
--------------------------------------------------------------------------------
1 | 'use client';
2 |
3 | import { useIntercom } from "react-use-intercom";
4 |
5 | export default function Home() {
6 | const { boot } = useIntercom();
7 |
8 | return (
9 |
10 | boot()}>Boot
11 |
12 | );
13 | }
--------------------------------------------------------------------------------
/apps/examples/nextjs-app-router/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "nextjs-app-router-example",
3 | "version": "0.1.0",
4 | "private": true,
5 | "scripts": {
6 | "dev": "next dev --port 3001",
7 | "build": "next build",
8 | "start": "next start"
9 | },
10 | "dependencies": {
11 | "next": "^14.0.0",
12 | "react": "^18.2.0",
13 | "react-dom": "^18.2.0",
14 | "react-use-intercom": "*"
15 | }
16 | }
--------------------------------------------------------------------------------
/apps/examples/nextjs-page-router/.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 | # next.js
12 | /.next/
13 | /out/
14 |
15 | # production
16 | /build
17 |
18 | # misc
19 | .DS_Store
20 | *.pem
21 |
22 | # debug
23 | npm-debug.log*
24 | yarn-debug.log*
25 | yarn-error.log*
26 |
27 | # local env files
28 | .env.local
29 | .env.development.local
30 | .env.test.local
31 | .env.production.local
32 |
33 | # vercel
34 | .vercel
35 |
--------------------------------------------------------------------------------
/apps/examples/nextjs-page-router/README.md:
--------------------------------------------------------------------------------
1 | # react-use-intercom in NextJS (Page Router)
2 |
3 | Replace `INTERCOM_APP_ID` with your Intercom app id.
4 |
--------------------------------------------------------------------------------
/apps/examples/nextjs-page-router/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "private": true,
3 | "name": "nextjs-page-router-example",
4 | "scripts": {
5 | "dev": "next dev",
6 | "build": "next build",
7 | "start": "next start"
8 | },
9 | "dependencies": {
10 | "next": "13.4.7",
11 | "react": "^18.2.0",
12 | "react-dom": "^18.2.0",
13 | "react-use-intercom": "*"
14 | }
15 | }
16 |
--------------------------------------------------------------------------------
/apps/examples/nextjs-page-router/pages/_app.js:
--------------------------------------------------------------------------------
1 | import '../styles/globals.css';
2 |
3 | import { IntercomProvider } from 'react-use-intercom';
4 |
5 | const INTERCOM_APP_ID = 'jcabc7e3';
6 |
7 | function MyApp({ Component, pageProps }) {
8 | return (
9 |
10 |
11 |
12 | );
13 | }
14 |
15 | export default MyApp;
16 |
--------------------------------------------------------------------------------
/apps/examples/nextjs-page-router/pages/api/hello.js:
--------------------------------------------------------------------------------
1 | // Next.js API route support: https://nextjs.org/docs/api-routes/introduction
2 |
3 | export default (req, res) => {
4 | res.statusCode = 200;
5 | res.json({ name: 'John Doe' });
6 | };
7 |
--------------------------------------------------------------------------------
/apps/examples/nextjs-page-router/pages/index.js:
--------------------------------------------------------------------------------
1 | import Head from 'next/head';
2 | import { useIntercom } from 'react-use-intercom';
3 |
4 | import styles from '../styles/Home.module.css';
5 |
6 | export default function Home() {
7 | const { boot } = useIntercom();
8 |
9 | return (
10 |
11 |
12 |
Create Next App
13 |
14 |
15 |
16 |
17 | boot()}>Boot
18 |
19 |
20 | );
21 | }
22 |
--------------------------------------------------------------------------------
/apps/examples/nextjs-page-router/public/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/devrnt/react-use-intercom/a89e1734584ac8421d8d81681be48e378786116f/apps/examples/nextjs-page-router/public/favicon.ico
--------------------------------------------------------------------------------
/apps/examples/nextjs-page-router/public/vercel.svg:
--------------------------------------------------------------------------------
1 |
3 |
4 |
--------------------------------------------------------------------------------
/apps/examples/nextjs-page-router/styles/Home.module.css:
--------------------------------------------------------------------------------
1 | .container {
2 | min-height: 100vh;
3 | padding: 0 0.5rem;
4 | display: flex;
5 | flex-direction: column;
6 | justify-content: center;
7 | align-items: center;
8 | }
9 |
10 | .main {
11 | padding: 5rem 0;
12 | flex: 1;
13 | display: flex;
14 | flex-direction: column;
15 | justify-content: center;
16 | align-items: center;
17 | }
18 |
19 | .footer {
20 | width: 100%;
21 | height: 100px;
22 | border-top: 1px solid #eaeaea;
23 | display: flex;
24 | justify-content: center;
25 | align-items: center;
26 | }
27 |
28 | .footer img {
29 | margin-left: 0.5rem;
30 | }
31 |
32 | .footer a {
33 | display: flex;
34 | justify-content: center;
35 | align-items: center;
36 | }
37 |
38 | .title a {
39 | color: #0070f3;
40 | text-decoration: none;
41 | }
42 |
43 | .title a:hover,
44 | .title a:focus,
45 | .title a:active {
46 | text-decoration: underline;
47 | }
48 |
49 | .title {
50 | margin: 0;
51 | line-height: 1.15;
52 | font-size: 4rem;
53 | }
54 |
55 | .title,
56 | .description {
57 | text-align: center;
58 | }
59 |
60 | .description {
61 | line-height: 1.5;
62 | font-size: 1.5rem;
63 | }
64 |
65 | .code {
66 | background: #fafafa;
67 | border-radius: 5px;
68 | padding: 0.75rem;
69 | font-size: 1.1rem;
70 | font-family: Menlo, Monaco, Lucida Console, Liberation Mono, DejaVu Sans Mono,
71 | Bitstream Vera Sans Mono, Courier New, monospace;
72 | }
73 |
74 | .grid {
75 | display: flex;
76 | align-items: center;
77 | justify-content: center;
78 | flex-wrap: wrap;
79 | max-width: 800px;
80 | margin-top: 3rem;
81 | }
82 |
83 | .card {
84 | margin: 1rem;
85 | flex-basis: 45%;
86 | padding: 1.5rem;
87 | text-align: left;
88 | color: inherit;
89 | text-decoration: none;
90 | border: 1px solid #eaeaea;
91 | border-radius: 10px;
92 | transition: color 0.15s ease, border-color 0.15s ease;
93 | }
94 |
95 | .card:hover,
96 | .card:focus,
97 | .card:active {
98 | color: #0070f3;
99 | border-color: #0070f3;
100 | }
101 |
102 | .card h3 {
103 | margin: 0 0 1rem 0;
104 | font-size: 1.5rem;
105 | }
106 |
107 | .card p {
108 | margin: 0;
109 | font-size: 1.25rem;
110 | line-height: 1.5;
111 | }
112 |
113 | .logo {
114 | height: 1em;
115 | }
116 |
117 | @media (max-width: 600px) {
118 | .grid {
119 | width: 100%;
120 | flex-direction: column;
121 | }
122 | }
123 |
--------------------------------------------------------------------------------
/apps/examples/nextjs-page-router/styles/globals.css:
--------------------------------------------------------------------------------
1 | html,
2 | body {
3 | padding: 0;
4 | margin: 0;
5 | font-family: -apple-system, BlinkMacSystemFont, Segoe UI, Roboto, Oxygen,
6 | Ubuntu, Cantarell, Fira Sans, Droid Sans, Helvetica Neue, sans-serif;
7 | }
8 |
9 | a {
10 | color: inherit;
11 | text-decoration: none;
12 | }
13 |
14 | * {
15 | box-sizing: border-box;
16 | }
17 |
--------------------------------------------------------------------------------
/apps/examples/vite/.gitignore:
--------------------------------------------------------------------------------
1 | # Logs
2 | logs
3 | *.log
4 | npm-debug.log*
5 | yarn-debug.log*
6 | yarn-error.log*
7 | pnpm-debug.log*
8 | lerna-debug.log*
9 |
10 | node_modules
11 | dist
12 | dist-ssr
13 | *.local
14 |
15 | # Editor directories and files
16 | .vscode/*
17 | !.vscode/extensions.json
18 | .idea
19 | .DS_Store
20 | *.suo
21 | *.ntvs*
22 | *.njsproj
23 | *.sln
24 | *.sw?
25 |
--------------------------------------------------------------------------------
/apps/examples/vite/README.md:
--------------------------------------------------------------------------------
1 | # react-use-intercom in a Vite app
2 |
3 | Replace `INTERCOM_APP_ID` with your Intercom app id.
4 |
--------------------------------------------------------------------------------
/apps/examples/vite/eslint.config.js:
--------------------------------------------------------------------------------
1 | import js from '@eslint/js'
2 | import globals from 'globals'
3 | import react from 'eslint-plugin-react'
4 | import reactHooks from 'eslint-plugin-react-hooks'
5 | import reactRefresh from 'eslint-plugin-react-refresh'
6 |
7 | export default [
8 | { ignores: ['dist'] },
9 | {
10 | files: ['**/*.{js,jsx}'],
11 | languageOptions: {
12 | ecmaVersion: 2020,
13 | globals: globals.browser,
14 | parserOptions: {
15 | ecmaVersion: 'latest',
16 | ecmaFeatures: { jsx: true },
17 | sourceType: 'module',
18 | },
19 | },
20 | settings: { react: { version: '18.3' } },
21 | plugins: {
22 | react,
23 | 'react-hooks': reactHooks,
24 | 'react-refresh': reactRefresh,
25 | },
26 | rules: {
27 | ...js.configs.recommended.rules,
28 | ...react.configs.recommended.rules,
29 | ...react.configs['jsx-runtime'].rules,
30 | ...reactHooks.configs.recommended.rules,
31 | 'react/jsx-no-target-blank': 'off',
32 | 'react-refresh/only-export-components': [
33 | 'warn',
34 | { allowConstantExport: true },
35 | ],
36 | },
37 | },
38 | ]
39 |
--------------------------------------------------------------------------------
/apps/examples/vite/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 | Vite + React
8 |
9 |
10 |
11 |
12 |
13 |
14 |
--------------------------------------------------------------------------------
/apps/examples/vite/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "vite-example",
3 | "private": true,
4 | "version": "0.0.0",
5 | "type": "module",
6 | "scripts": {
7 | "dev": "vite",
8 | "build": "vite build",
9 | "lint": "eslint .",
10 | "preview": "vite preview"
11 | },
12 | "dependencies": {
13 | "react": "^19.0.0",
14 | "react-dom": "^19.0.0",
15 | "react-use-intercom": "*"
16 | },
17 | "devDependencies": {
18 | "@eslint/js": "^9.19.0",
19 | "@types/react": "^19.0.8",
20 | "@types/react-dom": "^19.0.3",
21 | "@vitejs/plugin-react-swc": "^3.5.0",
22 | "eslint": "^9.19.0",
23 | "eslint-plugin-react": "^7.37.4",
24 | "eslint-plugin-react-hooks": "^5.0.0",
25 | "eslint-plugin-react-refresh": "^0.4.18",
26 | "globals": "^15.14.0",
27 | "vite": "^6.1.0"
28 | }
29 | }
30 |
--------------------------------------------------------------------------------
/apps/examples/vite/src/app.jsx:
--------------------------------------------------------------------------------
1 | import { useEffect } from 'react';
2 | import { IntercomProvider, useIntercom } from 'react-use-intercom';
3 |
4 | const INTERCOM_APP_ID = 'jcabc7e3';
5 |
6 | export function App() {
7 | return (
8 | alert('show')}
12 | >
13 |
14 |
15 | );
16 | }
17 |
18 | function OurApp() {
19 | return (
20 |
21 | Our App
22 |
23 |
24 | );
25 | }
26 |
27 | function TrackEvent() {
28 | const { trackEvent, boot, shutdown } = useIntercom();
29 |
30 | useEffect(() => {
31 | trackEvent('event');
32 | }, [trackEvent]);
33 |
34 | return (
35 | <>
36 | Tracing event in effect
37 | boot()}>Boot
38 | shutdown()}>Shutdown
39 | >
40 | );
41 | }
42 |
43 | export default App;
44 |
--------------------------------------------------------------------------------
/apps/examples/vite/src/main.jsx:
--------------------------------------------------------------------------------
1 | import { StrictMode } from 'react';
2 | import { createRoot } from 'react-dom/client';
3 |
4 | import { App } from './app.jsx';
5 |
6 | createRoot(document.getElementById('root')).render(
7 |
8 |
9 | ,
10 | );
11 |
--------------------------------------------------------------------------------
/apps/examples/vite/vite.config.js:
--------------------------------------------------------------------------------
1 | import { defineConfig } from 'vite';
2 | import react from '@vitejs/plugin-react-swc';
3 |
4 | // https://vite.dev/config/
5 | export default defineConfig({
6 | server: {
7 | port: 9300,
8 | },
9 | plugins: [react()],
10 | clearScreen: false,
11 | });
12 |
--------------------------------------------------------------------------------
/apps/playground/.gitignore:
--------------------------------------------------------------------------------
1 | /cypress/videos
2 | /cypress/screenshots
3 | /cypress/fixtures
4 |
--------------------------------------------------------------------------------
/apps/playground/assets/images/githubLogo.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
--------------------------------------------------------------------------------
/apps/playground/assets/images/logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/devrnt/react-use-intercom/a89e1734584ac8421d8d81681be48e378786116f/apps/playground/assets/images/logo.png
--------------------------------------------------------------------------------
/apps/playground/cypress.config.ts:
--------------------------------------------------------------------------------
1 | import { defineConfig } from 'cypress';
2 |
3 | export default defineConfig({
4 | // Enable cross-domain iframe access
5 | chromeWebSecurity: false,
6 | e2e: {
7 | baseUrl: 'http://localhost:5173/#/',
8 | video: false,
9 | specPattern: 'cypress/e2e/**/*.ts',
10 | },
11 | });
12 |
--------------------------------------------------------------------------------
/apps/playground/cypress/e2e/boot.ts:
--------------------------------------------------------------------------------
1 | ///
2 |
3 | describe('boot', () => {
4 | beforeEach(() => {
5 | cy.visit('/useIntercom');
6 |
7 | cy.intercept('https://api-iam.intercom.io/messenger/web/ping').as(
8 | 'intercomPing',
9 | );
10 | });
11 |
12 | afterEach(() => {
13 | cy.get('[data-cy=shutdown]').click();
14 | });
15 |
16 | it('should boot when calling `boot`', () => {
17 | cy.window().should('not.have.property', 'intercomSettings');
18 |
19 | cy.get('[data-cy=boot]').click();
20 |
21 | // Wait for the route aliased as 'intercomPing' to respond
22 | // without changing or stubbing its response
23 | cy.wait('@intercomPing');
24 |
25 | cy.get('.intercom-lightweight-app-launcher-icon-open').should('exist');
26 | cy.window().should('have.property', 'Intercom');
27 | cy.window().should('have.deep.property', 'intercomSettings', {
28 | app_id: 'jcabc7e3',
29 | });
30 | });
31 |
32 | it('should boot with seeded data when calling `boot`', () => {
33 | cy.window().should('not.have.property', 'intercomSettings');
34 |
35 | cy.get('[data-cy=boot-seeded]').click();
36 |
37 | cy.wait('@intercomPing');
38 |
39 | cy.get('.intercom-lightweight-app-launcher-icon-open').should('exist');
40 | cy.window().should('have.property', 'Intercom');
41 | cy.window().should('have.deep.property', 'intercomSettings', {
42 | app_id: 'jcabc7e3',
43 | name: 'Russo',
44 | });
45 | });
46 |
47 | it('should disable all methods before calling `boot`', () => {
48 | cy.get('[data-cy=update]').click();
49 | cy.get('.intercom-lightweight-app-launcher-icon-open').should('not.exist');
50 | cy.get('[data-cy=update-seeded]').click();
51 |
52 | cy.get('.intercom-lightweight-app-launcher-icon-open').should('not.exist');
53 | cy.get('[data-cy=show]').click();
54 | cy.get('.intercom-lightweight-app-launcher-icon-open').should('not.exist');
55 | });
56 | });
57 |
--------------------------------------------------------------------------------
/apps/playground/cypress/e2e/hide.ts:
--------------------------------------------------------------------------------
1 | ///
2 |
3 | describe('hide', () => {
4 | it('should hide when calling `hide`', () => {
5 | cy.visit('/useIntercom');
6 |
7 | cy.get('[data-cy=boot]').click();
8 |
9 | cy.get('[data-cy=show]').click();
10 | cy.get('iframe[name="intercom-messenger-frame"]').should('be.visible');
11 |
12 | cy.get('[data-cy=hide]').click();
13 | cy.get('iframe[name="intercom-messenger-frame"]').should('not.be.visible');
14 | });
15 | });
16 |
--------------------------------------------------------------------------------
/apps/playground/cypress/e2e/initializeDelay.ts:
--------------------------------------------------------------------------------
1 | ///
2 |
3 | describe('initialize delay', () => {
4 | it('should not load script on initial load with initialize delay', () => {
5 | cy.visit('/useIntercomWithTimeout');
6 |
7 | cy.document() //Important, because Cypress has its own Head
8 | .get('head script')
9 | .contains('[src=https://widget.intercom.io/widget/jcabc7e3]')
10 | .should('not.exist');
11 |
12 | cy.wait(5000);
13 |
14 | cy.document() //Important, because Cypress has its own Head
15 | .get('head script')
16 | .should('have.attr', 'src')
17 | .should('include', 'https://widget.intercom.io/widget/jcabc7e3');
18 | });
19 | });
20 |
--------------------------------------------------------------------------------
/apps/playground/cypress/e2e/mappers.ts:
--------------------------------------------------------------------------------
1 | ///
2 |
3 | describe('mappers', () => {
4 | // TODO: 'contain' does not work for objects (like avatar...)
5 | it('should map passed props to raw propped on `boot`', () => {
6 | cy.visit('/useIntercom');
7 |
8 | cy.window().should('not.have.property', 'intercomSettings');
9 |
10 | cy.get('[data-cy=boot-extended-seeded]').click();
11 |
12 | cy.window().should('have.property', 'Intercom');
13 |
14 | cy.window()
15 | .its('intercomSettings')
16 | .should('be.an', 'object')
17 | .and('contain', {
18 | app_id: 'jcabc7e3',
19 | name: 'Russo',
20 | action_color: 'red',
21 | utm_content: 'content',
22 | vertical_padding: 10,
23 | alignment: 'left',
24 | background_color: 'green',
25 | created_at: 'now',
26 | custom_launcher_selector: '.id',
27 | hide_default_launcher: false,
28 | horizontal_padding: 10,
29 | language_override: 'en',
30 | phone: '0470',
31 | session_duration: 1000,
32 | unsubscribed_from_emails: false,
33 | last_request_at: 'now',
34 | utm_campaign: 'campaign',
35 | utm_source: 'source',
36 | utm_medium: 'medium',
37 | utm_term: 'term',
38 | user_id: '12345',
39 | my_custom_attribute: 'custom_attribute_value',
40 | my_second_custom_attribute: 'second_custom_attribute_value',
41 | })
42 | .and('not.contain', {
43 | my_fifth_custom_attribute: 'custom_attribute_value',
44 | });
45 |
46 | cy.window()
47 | .its('intercomSettings')
48 | .its('avatar')
49 | .should('be.an', 'object')
50 | .and('deep.equal', {
51 | type: 'image',
52 | image_url: 'https://github.com/devrnt/react-use-intercom',
53 | });
54 |
55 | cy.window()
56 | .its('intercomSettings')
57 | .its('company')
58 | .should('be.an', 'object')
59 | .and('deep.equal', {
60 | company_id: 'company',
61 | created_at: 'now',
62 | industry: 'industry',
63 | monthly_spend: 10,
64 | name: 'name',
65 | plan: 'plan',
66 | size: 12,
67 | user_count: 100,
68 | website: 'https://github.com/devrnt/react-use-intercom',
69 | });
70 |
71 | cy.window()
72 | .its('intercomSettings')
73 | .its('companies')
74 | .should('be.an', 'array')
75 | .and('deep.equal', [
76 | {
77 | company_id: 'company',
78 | created_at: 'now',
79 | industry: 'industry',
80 | monthly_spend: 10,
81 | name: 'name',
82 | plan: 'plan',
83 | size: 12,
84 | user_count: 100,
85 | website: 'https://github.com/devrnt/react-use-intercom',
86 | },
87 | ]);
88 | });
89 | });
90 |
--------------------------------------------------------------------------------
/apps/playground/cypress/e2e/provider.ts:
--------------------------------------------------------------------------------
1 | ///
2 |
3 | beforeEach(() => {
4 | cy.intercept('https://api-iam.intercom.io/messenger/web/ping').as(
5 | 'intercomPing',
6 | );
7 | cy.intercept('https://api-iam.intercom.io/messenger/web/open').as(
8 | 'intercomOpen',
9 | );
10 | cy.intercept('https://api-iam.intercom.io/messenger/web/metrics').as(
11 | 'intercomMetrics',
12 | );
13 | cy.intercept('https://api-iam.intercom.io/messenger/web/messages').as(
14 | 'intercomMessages',
15 | );
16 | cy.intercept('https://api-iam.intercom.io/messenger/web/home').as(
17 | 'intercomHome',
18 | );
19 | cy.intercept('https://api-iam.intercom.io/messenger/web/conversations').as(
20 | 'intercomConversations',
21 | );
22 | });
23 |
24 | describe('provider', () => {
25 | it('should render children', () => {
26 | cy.visit('/provider');
27 |
28 | cy.get('p').should('be.visible');
29 | });
30 | });
31 |
32 | describe('provider with events', () => {
33 | beforeEach(() => {
34 | cy.visit('/providerEvents');
35 | });
36 |
37 | it('should render children', () => {
38 | cy.get('p').should('be.visible');
39 | });
40 |
41 | it('should render default text', () => {
42 | cy.contains('default');
43 | });
44 |
45 | it('should not execute `onShow` when not booted', () => {
46 | cy.get('[data-cy=onShowText]').should('have.text', 'default');
47 | cy.get('[data-cy=show]').click();
48 |
49 | cy.get('[data-cy=onShowText]').should('have.text', 'default');
50 | });
51 |
52 | it('should execute `onShow` event when clicking `show`', () => {
53 | cy.get('[data-cy=onShowText]').should('have.text', 'default');
54 | cy.get('[data-cy=boot]').click();
55 | cy.get('[data-cy=show]').click();
56 |
57 | cy.get('[data-cy=onShowText]').should('have.text', 'show was called');
58 | });
59 |
60 | it('should not execute `onHide` event when clicking `hide`', () => {
61 | cy.get('[data-cy=onHideText]').should('have.text', 'default');
62 | cy.get('[data-cy=boot]').click();
63 |
64 | cy.get('[data-cy=onHideText]').should('have.text', 'default');
65 | });
66 |
67 | it('should execute `onHide` event when clicking `hide`', () => {
68 | cy.get('[data-cy=onHideText]').should('have.text', 'default');
69 | cy.get('[data-cy=boot]').click();
70 | cy.get('[data-cy=show]').click();
71 | cy.get('[data-cy=hide]').click();
72 |
73 | cy.get('[data-cy=onHideText]').should('have.text', 'hide was called');
74 | });
75 |
76 | it('should execute `onUserEmailSupplied` event on `onUserEmailSupplied`', () => {
77 | cy.get('[data-cy=onUserEmailSuppliedText]').should('have.text', 'default');
78 |
79 | cy.get('[data-cy=boot]').click();
80 |
81 | cy.get('[data-cy=show]').click();
82 |
83 | cy.get('iframe[name="intercom-messenger-frame"]').then(($iframe) => {
84 | const $body = $iframe.contents().find('body');
85 |
86 | cy.wrap($body).contains('Send us a message').click();
87 |
88 | cy.wait('@intercomHome');
89 | cy.wait('@intercomConversations');
90 |
91 | cy.wrap($body)
92 | .find('textarea')
93 | .should('exist')
94 | .type('hello')
95 | .type('{enter}');
96 |
97 | cy.wait('@intercomMessages');
98 |
99 | cy.wrap($body).contains('Get notified by email', { timeout: 10000 });
100 |
101 | cy.wrap($body)
102 | .find('input[type="email"]')
103 | .type('hello@email.com')
104 | .type('{enter}');
105 |
106 | cy.get('[data-cy=onUserEmailSuppliedText]').should(
107 | 'have.text',
108 | 'on user email supplied was called',
109 | );
110 | });
111 | });
112 | });
113 |
114 | describe('provider with `apiBase`', () => {
115 | beforeEach(() => {
116 | cy.visit('/providerApi');
117 | });
118 |
119 | it('should set `api_base` if provided', () => {
120 | cy.get('p').should('be.visible');
121 |
122 | cy.window().should('have.deep.property', 'intercomSettings', {
123 | app_id: 'jcabc7e3',
124 | api_base: 'https://jcabc7e3.intercom-messenger.com',
125 | });
126 | });
127 | });
128 |
129 | describe('provider with `autoBootProps`', () => {
130 | beforeEach(() => {
131 | cy.visit('/providerAutoBootProps');
132 | });
133 |
134 | it('should set properties if passed to `autoBootProps` when `autoBoot` is `true`', () => {
135 | cy.get('p').should('be.visible');
136 |
137 | cy.get('span')
138 | .invoke('text')
139 | .then((phone) => {
140 | cy.window().should('have.deep.property', 'intercomSettings', {
141 | app_id: 'jcabc7e3',
142 | phone,
143 | });
144 | });
145 | });
146 | });
147 |
--------------------------------------------------------------------------------
/apps/playground/cypress/e2e/show.ts:
--------------------------------------------------------------------------------
1 | ///
2 |
3 | beforeEach(() => {
4 | cy.visit('/useIntercom');
5 |
6 | cy.intercept('https://api-iam.intercom.io/messenger/web/ping').as(
7 | 'intercomPing',
8 | );
9 | cy.intercept('https://api-iam.intercom.io/messenger/web/open').as(
10 | 'intercomOpen',
11 | );
12 | cy.intercept('https://api-iam.intercom.io/messenger/web/metrics').as(
13 | 'intercomMetrics',
14 | );
15 | });
16 |
17 | describe('show', () => {
18 | it('should show when calling `show`', () => {
19 | cy.get('[data-cy=boot]').click();
20 | cy.wait('@intercomPing');
21 |
22 | cy.get('[data-cy=show]').click();
23 | cy.wait('@intercomOpen');
24 |
25 | cy.get('iframe[name="intercom-messenger-frame"]')
26 | .should('be.visible')
27 | .then(($iframe) => {
28 | const $body = $iframe.contents().find('body');
29 |
30 | cy.wrap($body).contains('Send us a message');
31 | });
32 | });
33 | });
34 |
35 | describe('showMessages', () => {
36 | it('should show when calling `showMessages`', () => {
37 | cy.get('[data-cy=boot]').click();
38 |
39 | cy.get('[data-cy=show-messages]').click();
40 | cy.get('iframe[name="intercom-messenger-frame"]')
41 | .should('be.visible')
42 | .then(($iframe) => {
43 | const $body = $iframe.contents().find('body');
44 |
45 | cy.wrap($body).contains('Messages from the team will be shown here');
46 | });
47 | });
48 | });
49 |
50 | describe('showNewMessage', () => {
51 | it('should show new message `showNewMessage`', () => {
52 | cy.get('[data-cy=boot]').click();
53 | cy.wait('@intercomPing');
54 |
55 | cy.get('[data-cy=show-new-messages]').click();
56 | cy.wait('@intercomOpen');
57 |
58 | cy.get('iframe[name="intercom-messenger-frame"]')
59 | .should('be.visible')
60 | .then(($iframe) => {
61 | const $body = $iframe.contents().find('body');
62 |
63 | cy.wrap($body).contains('Ask us anything, or share your feedback.');
64 | cy.wrap($body).find('button[data-testid="go-back"]').should('exist');
65 | });
66 | });
67 |
68 | it('should show new message with content when calling `showNewMessage`', () => {
69 | cy.get('[data-cy=boot]').click();
70 | cy.wait('@intercomPing');
71 |
72 | cy.get('[data-cy=show-messages-content]').click();
73 | cy.wait('@intercomOpen');
74 |
75 | cy.get('iframe[name="intercom-messenger-frame"]')
76 | .should('be.visible')
77 | .then(($iframe) => {
78 | const $body = $iframe.contents().find('body');
79 |
80 | cy.wrap($body).find('button[data-testid="go-back"]').should('exist');
81 | cy.wrap($body)
82 | .find('textarea')
83 | .should('exist')
84 | .should('have.value', 'pre-definded-content');
85 | });
86 | });
87 | });
88 |
--------------------------------------------------------------------------------
/apps/playground/cypress/e2e/shutdown.ts:
--------------------------------------------------------------------------------
1 | ///
2 |
3 | describe('shutdown', () => {
4 | beforeEach(() => {
5 | cy.visit('/useIntercom');
6 | });
7 |
8 | afterEach(() => {
9 | cy.get('[data-cy=shutdown]').click();
10 | });
11 |
12 | it('should shutdown when calling `shutdown`', () => {
13 | cy.window().should('not.have.property', 'intercomSettings');
14 |
15 | cy.get('[data-cy=boot]').click();
16 | cy.get('[data-cy=shutdown]').click();
17 | cy.get('.intercom-lightweight-app-launcher-icon-open').should('not.exist');
18 | });
19 |
20 | it('should remove keep `window.intercomSettings` when calling `shutdown`', () => {
21 | cy.window().should('not.have.property', 'intercomSettings');
22 |
23 | cy.get('[data-cy=boot]').click();
24 | cy.get('[data-cy=shutdown]').click();
25 | cy.window().its('intercomSettings').should('be.undefined');
26 | cy.get('.intercom-lightweight-app-launcher-icon-open').should('not.exist');
27 | });
28 |
29 | it('should remove `app_id` when calling `hardShutdown`', () => {
30 | cy.window().should('not.have.property', 'intercomSettings');
31 |
32 | cy.get('[data-cy=boot]').click();
33 | cy.get('[data-cy=shutdown-hard]').click();
34 | cy.window().its('intercomSettings').should('be.undefined');
35 | cy.get('.intercom-lightweight-app-launcher-icon-open').should('not.exist');
36 | });
37 | });
38 |
--------------------------------------------------------------------------------
/apps/playground/cypress/e2e/startSurvey.ts:
--------------------------------------------------------------------------------
1 | ///
2 |
3 | before(() => {
4 | cy.visit('/useIntercom');
5 |
6 | cy.intercept('https://api-iam.intercom.io/messenger/web/ping').as(
7 | 'intercomPing',
8 | );
9 | cy.intercept('https://api-iam.intercom.io/messenger/web/open').as(
10 | 'intercomOpen',
11 | );
12 | cy.intercept('https://api-iam.intercom.io/messenger/web/metrics').as(
13 | 'intercomMetrics',
14 | );
15 | cy.intercept('https://api-iam.intercom.io/messenger/web/home_cards').as(
16 | 'intercomHomeCards',
17 | );
18 |
19 | cy.intercept('https://api-iam.intercom.io/messenger/web/surveys/*/fetch').as(
20 | 'intercomStartSurvey',
21 | );
22 | });
23 |
24 | describe('startSurvey', () => {
25 | it('start start the survey', () => {
26 | cy.get('[data-cy=boot]').click();
27 | cy.wait('@intercomPing');
28 |
29 | cy.get('[data-cy="start-survey"]').click();
30 |
31 | cy.wait(2000);
32 |
33 | cy.wait('@intercomStartSurvey');
34 |
35 | cy.get('iframe[name="intercom-banner-survey-frame"]').then(($iframe) => {
36 | const $body = $iframe.contents().find('body');
37 |
38 | cy.wrap($body).contains('What is the highest mountain in the world');
39 | });
40 | });
41 | });
42 |
--------------------------------------------------------------------------------
/apps/playground/cypress/e2e/update.ts:
--------------------------------------------------------------------------------
1 | ///
2 |
3 | describe('update', () => {
4 | afterEach(() => {
5 | cy.get('[data-cy=shutdown]').click();
6 | });
7 |
8 | it('should update with data when calling `update`', () => {
9 | cy.visit('/useIntercom');
10 |
11 | cy.get('[data-cy=boot-seeded]').click();
12 | cy.window().should('have.deep.property', 'intercomSettings', {
13 | app_id: 'jcabc7e3',
14 | name: 'Russo',
15 | });
16 | cy.get('[data-cy=update-seeded]').click();
17 | cy.window().should('have.deep.property', 'intercomSettings', {
18 | app_id: 'jcabc7e3',
19 | name: 'ponas',
20 | });
21 | });
22 | });
23 |
--------------------------------------------------------------------------------
/apps/playground/cypress/e2e/visitorId.ts:
--------------------------------------------------------------------------------
1 | ///
2 |
3 | before(() => {
4 | cy.visit('/useIntercom');
5 |
6 | cy.intercept('https://api-iam.intercom.io/messenger/web/ping').as(
7 | 'intercomPing',
8 | );
9 | cy.intercept('https://api-iam.intercom.io/messenger/web/open').as(
10 | 'intercomOpen',
11 | );
12 | cy.intercept('https://api-iam.intercom.io/messenger/web/metrics').as(
13 | 'intercomMetrics',
14 | );
15 | cy.intercept('https://api-iam.intercom.io/messenger/web/home_cards').as(
16 | 'intercomHomeCards',
17 | );
18 | });
19 |
20 | describe('getVisitorId', () => {
21 | it('should get visitor id when calling `getVisitorId`', () => {
22 | cy.get('[data-cy=boot]').click();
23 | cy.wait('@intercomPing');
24 |
25 | cy.get('button[data-cy="visitorId"]').click();
26 |
27 | cy.get('p[data-cy="visitorIdValue"]').should('exist');
28 | });
29 | });
30 |
--------------------------------------------------------------------------------
/apps/playground/cypress/support/e2e.ts:
--------------------------------------------------------------------------------
1 | // Add e2e commands here
2 |
--------------------------------------------------------------------------------
/apps/playground/cypress/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "strict": true,
4 | },
5 | "files": ["*.ts"]
6 | }
7 |
--------------------------------------------------------------------------------
/apps/playground/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/devrnt/react-use-intercom/a89e1734584ac8421d8d81681be48e378786116f/apps/playground/favicon.ico
--------------------------------------------------------------------------------
/apps/playground/global.d.ts:
--------------------------------------------------------------------------------
1 | declare module '*.png' {
2 | const value: any;
3 | export default value;
4 | }
5 |
6 | declare module '*.svg' {
7 | const value: any;
8 | export default value;
9 | }
10 |
--------------------------------------------------------------------------------
/apps/playground/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 | react-use-intercom
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
--------------------------------------------------------------------------------
/apps/playground/index.tsx:
--------------------------------------------------------------------------------
1 | import * as React from 'react';
2 | import * as ReactDOM from 'react-dom';
3 |
4 | import App from './src/app';
5 |
6 | const Root = () => ;
7 |
8 | ReactDOM.render( , document.getElementById('root'));
9 |
--------------------------------------------------------------------------------
/apps/playground/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "playground",
3 | "license": "MIT",
4 | "private": true,
5 | "homepage": "https://devrnt.github.io/react-use-intercom/",
6 | "scripts": {
7 | "dev": "vite",
8 | "build": "tsc && vite build",
9 | "e2e": "cypress run",
10 | "e2e:open": "cypress open",
11 | "preview": "vite preview --host"
12 | },
13 | "dependencies": {
14 | "react": "^18.2.0",
15 | "react-dom": "^18.2.0",
16 | "react-router-dom": "^5.2.0",
17 | "react-use-intercom": "workspace:*",
18 | "styled-components": "^5.3.6",
19 | "styled-normalize": "^8.0.7"
20 | },
21 | "devDependencies": {
22 | "@types/react": "^18.0.27",
23 | "@types/react-dom": "^18.0.10",
24 | "@types/react-router-dom": "^5.1.5",
25 | "@types/styled-components": "^5.1.2",
26 | "@vitejs/plugin-react": "^3.0.1",
27 | "cypress": "^14.0.0",
28 | "typescript": "^5.7.3",
29 | "vite": "^4.5.14"
30 | }
31 | }
32 |
--------------------------------------------------------------------------------
/apps/playground/src/app.tsx:
--------------------------------------------------------------------------------
1 | import * as React from 'react';
2 | import { HashRouter as Router, NavLink, Route } from 'react-router-dom';
3 | import styled from 'styled-components';
4 |
5 | import {
6 | ProviderApiPage,
7 | ProviderAutoBootProps,
8 | ProviderEventsPage,
9 | ProviderPage,
10 | UseIntercomPage,
11 | UseIntercomTourPage,
12 | UseIntercomWithDelay,
13 | } from './modules';
14 | import { Page, Style } from './modules/common';
15 |
16 | const Navigation = styled.ul`
17 | padding: 0;
18 | display: grid;
19 | grid-template-columns: 1fr;
20 | grid-row-gap: 1.75rem;
21 | `;
22 |
23 | const Link = styled(NavLink)`
24 | text-decoration: none;
25 | color: var(--dark);
26 |
27 | &:visited,
28 | &:active {
29 | text-decoration: none;
30 | }
31 |
32 | > code {
33 | font-size: 1rem;
34 | }
35 | `;
36 |
37 | const App = () => {
38 | return (
39 | <>
40 |
41 |
45 |
46 |
47 |
48 |
49 |
53 |
54 |
55 |
59 |
60 |
61 |
62 | IntercomProvider
63 |
64 |
65 | IntercomProvider with event callbacks
66 |
67 |
68 | useIntercom
69 |
70 |
71 | useIntercom with tour
72 |
73 |
74 | useIntercom with delayed boot
75 |
76 |
77 |
78 |
79 |
80 | >
81 | );
82 | };
83 |
84 | export default App;
85 |
--------------------------------------------------------------------------------
/apps/playground/src/modules/common/button.tsx:
--------------------------------------------------------------------------------
1 | import * as React from 'react';
2 | import styled from 'styled-components';
3 |
4 | type Props = {
5 | label: string;
6 | } & React.HTMLAttributes;
7 |
8 | const Container = styled.button`
9 | width: fit-content;
10 | min-width: 8rem;
11 | border: none;
12 | padding: 0.75rem 1.55rem;
13 | border-radius: 6px;
14 | color: white;
15 | font-size: 0.9rem;
16 | font-weight: 700;
17 | background-color: var(--dark);
18 | cursor: pointer;
19 |
20 | &:active,
21 | &:focus,
22 | &:hover {
23 | background-image: linear-gradient(to right, #45e87b, #05e1f9);
24 | }
25 | `;
26 |
27 | const Button = ({ label, ...rest }: Props) => (
28 | {label}
29 | );
30 |
31 | export default Button;
32 |
--------------------------------------------------------------------------------
/apps/playground/src/modules/common/index.ts:
--------------------------------------------------------------------------------
1 | export { default as Button } from './button';
2 | export { default as Page } from './page';
3 | export { default as Style } from './style';
4 |
--------------------------------------------------------------------------------
/apps/playground/src/modules/common/page.tsx:
--------------------------------------------------------------------------------
1 | import * as React from 'react';
2 | import styled from 'styled-components';
3 |
4 | import githubLogoUrl from '../../../assets/images/githubLogo.svg';
5 | import logoUrl from '../../../assets/images/logo.png';
6 |
7 | type Props = {
8 | title: string;
9 | description?: string;
10 | children: React.ReactNode;
11 | };
12 |
13 | const Container = styled.main`
14 | display: flex;
15 | justify-content: center;
16 | padding: 1.5rem 0;
17 | `;
18 |
19 | const Wrapper = styled.div`
20 | max-width: 1024px;
21 | padding: 0 2rem;
22 | width: 100%;
23 | `;
24 |
25 | const Body = styled.div`
26 | margin-top: 2rem;
27 | `;
28 |
29 | const Description = styled.div`
30 | color: var(--dark);
31 | font-size: 1.1rem;
32 | font-weight: 400;
33 | `;
34 |
35 | const Divider = styled.div`
36 | height: 2px;
37 | width: 5%;
38 | background-image: linear-gradient(to right, #45e87b, #05e1f9);
39 | margin: 2.5rem 0;
40 | `;
41 |
42 | const TopBar = styled.header`
43 | display: flex;
44 | justify-content: space-between;
45 | align-items: center;
46 | margin-bottom: 2rem;
47 | `;
48 |
49 | const Logo = styled.img`
50 | height: 3rem;
51 | width: 3rem;
52 | `;
53 |
54 | const GithubLogo = styled.img`
55 | width: 2rem;
56 | `;
57 |
58 | const Page = ({ title, description, children }: Props) => {
59 | return (
60 |
61 |
62 |
63 |
64 |
69 |
70 |
71 |
72 | {title}
73 | {description}
74 |
75 | {children}
76 |
77 |
78 | );
79 | };
80 |
81 | export default Page;
82 |
--------------------------------------------------------------------------------
/apps/playground/src/modules/common/style.tsx:
--------------------------------------------------------------------------------
1 | import * as React from 'react';
2 | import { createGlobalStyle } from 'styled-components';
3 | import { Normalize } from 'styled-normalize';
4 |
5 | const GlobalStyle = createGlobalStyle`
6 | :root {
7 | --light: #ffffff;
8 | --dark: #181818;
9 | --cyan: #6afdef;
10 | --grey: #f4f0eb;
11 | }
12 |
13 | html {
14 | font-size: 15px;
15 | }
16 |
17 | h1,
18 | h2,
19 | h3,
20 | h4,
21 | h5,
22 | h6,
23 | p {
24 | color: var(--dark);
25 | line-height: 1.75rem;
26 | }
27 |
28 | body {
29 | font-family: 'Hind', Helvetica, sans-serif;
30 | text-rendering: optimizeLegibility;
31 | }
32 |
33 | code {
34 | background: var(--grey);
35 | border-radius: 2px;
36 | font-family: 'Fira Code', monospace;
37 | font-size: 0.825rem;
38 | padding: 0.075rem 0.35rem;
39 | }
40 | `;
41 |
42 | const Style = () => (
43 | <>
44 |
45 |
46 | >
47 | );
48 |
49 | export default Style;
50 |
--------------------------------------------------------------------------------
/apps/playground/src/modules/index.ts:
--------------------------------------------------------------------------------
1 | export * from './provider';
2 | export * from './useIntercom';
3 |
--------------------------------------------------------------------------------
/apps/playground/src/modules/provider/index.ts:
--------------------------------------------------------------------------------
1 | export { default as ProviderPage } from './provider';
2 | export { default as ProviderEventsPage } from './providerEvents';
3 | export { default as ProviderApiPage } from './providerApi';
4 | export { default as ProviderAutoBootProps } from './providerAutoBootProps';
5 |
--------------------------------------------------------------------------------
/apps/playground/src/modules/provider/provider.tsx:
--------------------------------------------------------------------------------
1 | import * as React from 'react';
2 | import { IntercomProvider } from 'react-use-intercom';
3 |
4 | const Provider = () => {
5 | return (
6 |
7 | Intercom children
8 |
9 | );
10 | };
11 |
12 | export default Provider;
13 |
--------------------------------------------------------------------------------
/apps/playground/src/modules/provider/providerApi.tsx:
--------------------------------------------------------------------------------
1 | import * as React from 'react';
2 | import { IntercomProvider } from 'react-use-intercom';
3 |
4 | const Provider = () => {
5 | return (
6 |
11 | Intercom children
12 |
13 | );
14 | };
15 |
16 | export default Provider;
17 |
--------------------------------------------------------------------------------
/apps/playground/src/modules/provider/providerAutoBootProps.tsx:
--------------------------------------------------------------------------------
1 | import * as React from 'react';
2 |
3 | import { IntercomProvider } from 'react-use-intercom';
4 |
5 | const ProviderAutoBootProps = () => {
6 | const phone = '123456';
7 |
8 | return (
9 |
10 |
11 | Intercom children, phone: {phone}
12 |
13 |
14 | );
15 | };
16 |
17 | export default ProviderAutoBootProps;
18 |
--------------------------------------------------------------------------------
/apps/playground/src/modules/provider/providerEvents.tsx:
--------------------------------------------------------------------------------
1 | import * as React from 'react';
2 | import { IntercomProvider, useIntercom } from 'react-use-intercom';
3 | import styled from 'styled-components';
4 |
5 | import { Button } from '../common';
6 |
7 | const Grid = styled.div`
8 | display: grid;
9 | grid-template-columns: repeat(1, 1fr);
10 | width: 100%;
11 | `;
12 |
13 | const Item = styled.div`
14 | display: grid;
15 | grid-template-rows: min-content;
16 |
17 | &::after {
18 | content: '';
19 | margin: 2rem 0 1.5rem;
20 | border-bottom: 2px solid var(--grey);
21 | width: 100%;
22 | }
23 | `;
24 |
25 | type RawProviderEventsProps = {
26 | onShowText: string;
27 | onHideText: string;
28 | onUserEmailSuppliedText: string;
29 | };
30 |
31 | const RawProviderEvents = ({
32 | onShowText,
33 | onHideText,
34 | onUserEmailSuppliedText,
35 | }: RawProviderEventsProps) => {
36 | const { boot, show, hide } = useIntercom();
37 |
38 | return (
39 | <>
40 | Intercom children
41 | {onShowText}
42 | {onHideText}
43 | {onUserEmailSuppliedText}
44 |
45 |
46 | -
47 |
Just boot the Intercom
48 | boot()} />
49 |
50 | -
51 |
click should update the `onShow` text
52 |
53 |
54 |
55 | -
56 |
click should update the `onHide` text
57 |
58 |
59 |
60 | >
61 | );
62 | };
63 |
64 | const ProviderEvents = () => {
65 | const [onShowText, setOnShowText] = React.useState('default');
66 | const [onHideText, setHideText] = React.useState('default');
67 | const [onUserEmailSuppliedText, setOnUserEmailSupplied] =
68 | React.useState('default');
69 |
70 | const onShow = React.useCallback(() => setOnShowText('show was called'), []);
71 | const onHide = React.useCallback(() => setHideText('hide was called'), []);
72 | const onUserEmailSupplied = () =>
73 | setOnUserEmailSupplied('on user email supplied was called');
74 |
75 | return (
76 |
82 |
87 |
88 | );
89 | };
90 |
91 | export default ProviderEvents;
92 |
--------------------------------------------------------------------------------
/apps/playground/src/modules/useIntercom/index.ts:
--------------------------------------------------------------------------------
1 | export { default as UseIntercomPage } from './useIntercom';
2 | export { default as UseIntercomTourPage } from './useIntercomTour';
3 | export { default as UseIntercomWithDelay } from './useIntercomWithDelay';
4 |
--------------------------------------------------------------------------------
/apps/playground/src/modules/useIntercom/useIntercom.tsx:
--------------------------------------------------------------------------------
1 | import * as React from 'react';
2 | import { IntercomProvider, useIntercom } from 'react-use-intercom';
3 | import styled from 'styled-components';
4 |
5 | import { Button } from '../common';
6 |
7 | const Grid = styled.div`
8 | display: grid;
9 | grid-template-columns: repeat(1, 1fr);
10 | width: 100%;
11 | `;
12 |
13 | const Item = styled.div`
14 | display: grid;
15 | grid-template-rows: min-content;
16 |
17 | &::after {
18 | content: '';
19 | margin: 2rem 0 1.5rem;
20 | border-bottom: 2px solid var(--grey);
21 | width: 100%;
22 | }
23 | `;
24 |
25 | const RawUseIntercomPage = () => {
26 | const [visitorId, setVisitorId] = React.useState(null);
27 |
28 | const {
29 | boot,
30 | shutdown,
31 | hardShutdown,
32 | update,
33 | hide,
34 | show,
35 | showMessages,
36 | showNewMessage,
37 | getVisitorId,
38 | trackEvent,
39 | showArticle,
40 | startSurvey,
41 | showSpace,
42 | showNews,
43 | } = useIntercom();
44 | const handleBoot = React.useCallback(() => boot(), [boot]);
45 |
46 | const handleSeededBoot = React.useCallback(
47 | () => boot({ name: 'Russo' }),
48 | [boot],
49 | );
50 |
51 | const handleExtendedSeededBoot = React.useCallback(
52 | () =>
53 | boot({
54 | name: 'Russo',
55 | actionColor: 'red',
56 | email: 'russo@email.com',
57 | utmContent: 'content',
58 | verticalPadding: 10,
59 | alignment: 'left',
60 | avatar: {
61 | type: 'image',
62 | imageUrl: 'https://github.com/devrnt/react-use-intercom',
63 | },
64 | company: {
65 | companyId: 'company',
66 | createdAt: 'now',
67 | industry: 'industry',
68 | monthlySpend: 10,
69 | name: 'name',
70 | plan: 'plan',
71 | size: 12,
72 | userCount: 100,
73 | website: 'https://github.com/devrnt/react-use-intercom',
74 | },
75 | companies: [
76 | {
77 | companyId: 'company',
78 | createdAt: 'now',
79 | industry: 'industry',
80 | monthlySpend: 10,
81 | name: 'name',
82 | plan: 'plan',
83 | size: 12,
84 | userCount: 100,
85 | website: 'https://github.com/devrnt/react-use-intercom',
86 | },
87 | ],
88 | backgroundColor: 'green',
89 | createdAt: 'now',
90 | customLauncherSelector: '.id',
91 | hideDefaultLauncher: false,
92 | horizontalPadding: 10,
93 | languageOverride: 'en',
94 | phone: '0470',
95 | sessionDuration: 1000,
96 | unsubscribedFromEmails: false,
97 | userHash: '123',
98 | lastRequestAt: 'now',
99 | utmCampaign: 'campaign',
100 | utmSource: 'source',
101 | utmMedium: 'medium',
102 | utmTerm: 'term',
103 | userId: '12345',
104 | customAttributes: {
105 | my_custom_attribute: 'custom_attribute_value',
106 | my_second_custom_attribute: 'second_custom_attribute_value',
107 | },
108 | }),
109 | [boot],
110 | );
111 |
112 | const handleUpdate = React.useCallback(() => {
113 | update();
114 | }, [update]);
115 |
116 | const handleSeededUpdate = React.useCallback(() => {
117 | update({ name: 'ponas' });
118 | }, [update]);
119 |
120 | const handleNewMessages = React.useCallback(
121 | () => showNewMessage(),
122 | [showNewMessage],
123 | );
124 |
125 | const handleNewMessagesWithContent = React.useCallback(
126 | () => showNewMessage('pre-definded-content'),
127 | [showNewMessage],
128 | );
129 |
130 | const handleGetVisitorId = React.useCallback(() => {
131 | const id = getVisitorId();
132 | setVisitorId(id);
133 | }, [getVisitorId]);
134 |
135 | const handleTrackEvent = React.useCallback(() => {
136 | trackEvent('invited-friend');
137 | }, [trackEvent]);
138 |
139 | const handleTrackEventWithMetaData = React.useCallback(() => {
140 | trackEvent('invited-friend', { name: 'Russo' });
141 | }, [trackEvent]);
142 |
143 | const handleShowArticle = React.useCallback(() => {
144 | showArticle(4013997);
145 | }, [showArticle]);
146 |
147 | const handleShowSpace = React.useCallback(() => {
148 | showSpace('messages');
149 | }, [showSpace]);
150 |
151 | const handleStartSurvey = () => startSurvey(45288402);
152 |
153 | const handleShowNews = React.useCallback(() => {
154 | showNews(33910172);
155 | }, [showNews]);
156 |
157 | return (
158 |
159 | -
160 |
161 | boots the Intercom instance, not needed if autoBoot
in{' '}
162 | IntercomProvider
is true
163 |
164 |
165 |
166 | -
167 |
168 | boots the Intercom instance with given props
169 |
170 |
175 |
176 | -
177 |
178 | boots the Intercom instance with given extended props
179 |
180 |
185 |
186 | -
187 |
shuts down the Intercom instance
188 |
189 |
190 | -
191 |
192 | same functionality as shutdown
, but makes sure the
193 | Intercom cookies, window.Intercom
and{' '}
194 | window.intercomSettings
are removed
195 |
196 |
201 |
202 | -
203 |
Initiates a 'ping'
204 |
205 |
206 | -
207 |
208 | updates the Intercom instance with the supplied props
209 |
210 |
215 |
216 | -
217 |
shows the Messenger
218 |
219 |
220 | -
221 |
hides the Messenger
222 |
223 |
224 | -
225 |
shows the Messenger with the message list
226 |
231 |
232 | -
233 |
shows the Messenger as if a new conversation was just created.
234 |
239 |
240 | -
241 |
242 | shows the Messenger as if a new conversation was just created with the
243 | prefilled content
244 |
245 |
250 |
251 | -
252 |
gets the visitor id
253 |
258 | {visitorId ? {visitorId}
: null}
259 |
260 | -
261 |
262 | submits an event
{' '}
263 |
264 |
265 |
266 | -
267 |
268 | submits an event
with metadata
269 |
270 |
274 |
275 | -
276 |
277 | opens an article with the given articleId
278 |
279 |
280 |
281 | -
282 |
283 | start survey with the given surveyId
284 |
285 |
290 |
291 | -
292 |
293 | opens the Messenger with the given space
294 |
295 |
296 |
297 | -
298 |
299 | opens the Messenger with the given newsId
300 |
301 |
302 |
303 |
304 | );
305 | };
306 |
307 | const UseIntercomPage = () => {
308 | return (
309 |
310 |
311 |
312 | );
313 | };
314 |
315 | export default UseIntercomPage;
316 |
--------------------------------------------------------------------------------
/apps/playground/src/modules/useIntercom/useIntercomTour.tsx:
--------------------------------------------------------------------------------
1 | import * as React from 'react';
2 | import styled from 'styled-components';
3 |
4 | import { IntercomProvider, useIntercom } from 'react-use-intercom';
5 | import { Button } from '../common';
6 |
7 | const Grid = styled.div`
8 | display: grid;
9 | grid-template-columns: repeat(1, 1fr);
10 | width: 100%;
11 | `;
12 |
13 | const Item = styled.div`
14 | display: grid;
15 | grid-template-rows: min-content;
16 |
17 | &::after {
18 | content: '';
19 | margin: 2rem 0 1.5rem;
20 | border-bottom: 2px solid var(--grey);
21 | width: 100%;
22 | }
23 | `;
24 |
25 | const RawUseIntercomStartPagePage = () => {
26 | const { boot, shutdown, hardShutdown, startTour } = useIntercom();
27 |
28 | const handleBoot = React.useCallback(() => boot({ name: 'Russo' }), [boot]);
29 |
30 | const handleStartTour = React.useCallback(() => {
31 | startTour(124247);
32 | }, [startTour]);
33 |
34 | return (
35 |
36 | -
37 |
38 | starts a tour based on the tourId
39 |
40 |
41 | Start tour
42 |
43 |
44 | -
45 |
46 | boots the Intercom instance, not needed if autoBoot
in{' '}
47 | IntercomProvider
is true
48 |
49 | boot()} />
50 |
51 | -
52 |
53 | boots the Intercom instance with given props
54 |
55 |
56 |
57 | -
58 |
shuts down the Intercom instance
59 |
60 |
61 | -
62 |
63 | same functionality as shutdown
, but makes sure the
64 | Intercom cookies, window.Intercom
and{' '}
65 | window.intercomSettings
are removed
66 |
67 |
72 |
73 |
74 | );
75 | };
76 |
77 | const UseIntercomTourPage = () => {
78 | return (
79 |
80 |
81 |
82 | );
83 | };
84 |
85 | export default UseIntercomTourPage;
86 |
--------------------------------------------------------------------------------
/apps/playground/src/modules/useIntercom/useIntercomWithDelay.tsx:
--------------------------------------------------------------------------------
1 | import * as React from 'react';
2 | import { IntercomProvider } from 'react-use-intercom';
3 | import styled from 'styled-components';
4 |
5 | const Grid = styled.div`
6 | display: grid;
7 | grid-template-columns: repeat(1, 1fr);
8 | width: 100%;
9 | `;
10 |
11 | const Item = styled.div`
12 | display: grid;
13 | grid-template-rows: min-content;
14 |
15 | &::after {
16 | content: '';
17 | margin: 2rem 0 1.5rem;
18 | border-bottom: 2px solid var(--grey);
19 | width: 100%;
20 | }
21 | `;
22 |
23 | const RawUseIntercomPage = () => {
24 | return (
25 |
26 | -
27 |
Intercom will be initialized (and autobooted) after 5000ms
28 |
29 |
30 | );
31 | };
32 |
33 | const UseIntercomWithDelayPage = () => {
34 | return (
35 |
36 |
37 |
38 | );
39 | };
40 |
41 | export default UseIntercomWithDelayPage;
42 |
--------------------------------------------------------------------------------
/apps/playground/src/vite-env.d.ts:
--------------------------------------------------------------------------------
1 | ///
2 |
--------------------------------------------------------------------------------
/apps/playground/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "target": "ESNext",
4 | "useDefineForClassFields": true,
5 | "lib": ["DOM", "DOM.Iterable", "ESNext"],
6 | "allowJs": false,
7 | "skipLibCheck": true,
8 | "esModuleInterop": false,
9 | "allowSyntheticDefaultImports": true,
10 | "strict": true,
11 | "forceConsistentCasingInFileNames": true,
12 | "module": "ESNext",
13 | "moduleResolution": "bundler",
14 | "resolveJsonModule": true,
15 | "isolatedModules": true,
16 | "noEmit": true,
17 | "jsx": "react-jsx",
18 | },
19 | "include": ["src", "vite.config.ts"],
20 | }
21 |
--------------------------------------------------------------------------------
/apps/playground/vite.config.ts:
--------------------------------------------------------------------------------
1 | import react from '@vitejs/plugin-react';
2 | import { defineConfig } from 'vite';
3 |
4 | // https://vitejs.dev/config/
5 | export default defineConfig({
6 | // https://vitejs.dev/guide/static-deploy.html#github-pages
7 | base: '/react-use-intercom/',
8 | preview: {
9 | port: 5173,
10 | },
11 | plugins: [react()],
12 | });
13 |
--------------------------------------------------------------------------------
/assets/logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/devrnt/react-use-intercom/a89e1734584ac8421d8d81681be48e378786116f/assets/logo.png
--------------------------------------------------------------------------------
/eslint.config.js:
--------------------------------------------------------------------------------
1 | import eslint from '@eslint/js';
2 | import prettierConfig from 'eslint-config-prettier';
3 | import reactPlugin from 'eslint-plugin-react';
4 | import reactHooks from 'eslint-plugin-react-hooks';
5 | import simpleImportSort from 'eslint-plugin-simple-import-sort';
6 | import tseslint from 'typescript-eslint';
7 |
8 | export default tseslint.config(
9 | eslint.configs.recommended,
10 | tseslint.configs.recommended,
11 | prettierConfig,
12 | {
13 | files: ['**/*.{js,jsx,ts,tsx}'],
14 | plugins: {
15 | react: reactPlugin,
16 | 'react-hooks': reactHooks,
17 | 'simple-import-sort': simpleImportSort,
18 | },
19 | languageOptions: {
20 | parserOptions: {
21 | ecmaFeatures: {
22 | jsx: true,
23 | },
24 | },
25 | },
26 | settings: {
27 | react: {
28 | version: 'detect',
29 | },
30 | },
31 | rules: {
32 | ...reactHooks.configs.recommended.rules,
33 | 'simple-import-sort/imports': 'error',
34 | 'sort-imports': 'off',
35 | 'import/order': 'off',
36 | 'import/no-anonymous-default-export': 'off',
37 | '@typescript-eslint/no-explicit-any': 'off',
38 | },
39 | },
40 | );
41 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "private": true,
3 | "packageManager": "pnpm@10.2.0",
4 | "engines": {
5 | "node": "^22.13.1",
6 | "pnpm": "^10.2.0"
7 | },
8 | "type": "module",
9 | "scripts": {
10 | "dev": "turbo run dev --parallel --filter=playground --filter=react-use-intercom",
11 | "dev:examples": "turbo run dev --parallel --filter=*-example --filter=react-use-intercom",
12 | "test": "turbo run test",
13 | "build": "turbo run build",
14 | "build:playground": "turbo run build --filter=playground",
15 | "lint": "turbo run lint",
16 | "lint:fix": "turbo run lint:fix",
17 | "clean": "turbo run clean && rm -rf node_modules",
18 | "e2e": "turbo run e2e --filter=playground",
19 | "e2e:open": "turbo run e2e:open --filter=playground",
20 | "changeset": "changeset",
21 | "release": "turbo run build --filter=react-use-intercom && changeset publish"
22 | },
23 | "devDependencies": {
24 | "@changesets/cli": "^2.26.0",
25 | "@eslint/js": "^9.20.0",
26 | "@typescript-eslint/eslint-plugin": "^8.23.0",
27 | "eslint": "^9.20.0",
28 | "eslint-config-prettier": "^10.0.1",
29 | "eslint-plugin-prettier": "^5.2.3",
30 | "eslint-plugin-react-hooks": "^5.0.0",
31 | "eslint-plugin-simple-import-sort": "^12.1.1",
32 | "prettier": "^3.4.2",
33 | "turbo": "^2.4.0",
34 | "typescript": "^5.7.3",
35 | "typescript-eslint": "^8.23.0"
36 | }
37 | }
38 |
--------------------------------------------------------------------------------
/packages/react-use-intercom/.gitignore:
--------------------------------------------------------------------------------
1 | /coverage/
2 |
--------------------------------------------------------------------------------
/packages/react-use-intercom/.npmignore:
--------------------------------------------------------------------------------
1 | src
2 |
--------------------------------------------------------------------------------
/packages/react-use-intercom/CHANGELOG.md:
--------------------------------------------------------------------------------
1 | # react-use-intercom
2 |
3 | ## 5.4.3
4 |
5 | ### Patch Changes
6 |
7 | - 0850d55: Add support for Content-Security-Policy nonce
8 |
9 | ## 5.4.2
10 |
11 | ### Patch Changes
12 |
13 | - 0d8353d: Add support for zIndex launcher property
14 | - fec6233: Fix initializing intercom api
15 |
16 | ## 5.4.1
17 |
18 | ### Patch Changes
19 |
20 | - a6d7757: Re-attach callbacks in boot method
21 |
22 | ## 5.4.0
23 |
24 | ### Minor Changes
25 |
26 | - 8d5d130: Exposes showTicket and showConversation methods in useIntercom hook
27 |
28 | ## 5.3.0
29 |
30 | ### Minor Changes
31 |
32 | - 4aac5a9: Expose startChecklist method in useIntercom hook
33 |
34 | ## 5.2.0
35 |
36 | ### Minor Changes
37 |
38 | - 8eebbed: Add "showNews" method
39 |
40 | ## 5.1.4
41 |
42 | ### Patch Changes
43 |
44 | - d86105a: Fix incompatible node engine version
45 |
46 | ## 5.1.3
47 |
48 | ### Patch Changes
49 |
50 | - e89a073: Do not publish src folder to registry
51 |
52 | ## 5.1.2
53 |
54 | ### Patch Changes
55 |
56 | - 5f4b6eb: Do not publish src folder to registry
57 |
58 | ## 5.1.1
59 |
60 | ### Patch Changes
61 |
62 | - 5e7dd7e: Do not publish src folder to registry
63 |
64 | ## 5.1.0
65 |
66 | ### Minor Changes
67 |
68 | - e30828d: Add "showSpace" method
69 |
70 | ## 5.0.0
71 |
72 | ### Major Changes
73 |
74 | - 8f7ff67: Allow calling "boot" multiple times. Previously it was not possible to invoke "boot" more than once, because of a "isBooted" guard. This behaviour was not in line with the IntercomJS docs, so it has been removed
75 |
76 | ## 4.1.0
77 |
78 | ### Minor Changes
79 |
80 | - 791c6ac: Extend API with `onUserEmailSupplied` event and `startSurvey` method
81 |
82 | ## 4.0.0
83 |
84 | ### Major Changes
85 |
86 | - c8b050d: Update built target to es2017
87 |
--------------------------------------------------------------------------------
/packages/react-use-intercom/README.md:
--------------------------------------------------------------------------------
1 |
2 | react-use-intercom
3 | A React Intercom integration powered by hooks.
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 | ## Features
14 | * Hooks
15 | * Written in TypeScript
16 | * Documented, self explaining methods
17 | * [Tiny size](https://bundlephobia.com/result?p=react-use-intercom@latest) without any external libraries
18 | * Safeguard for SSR environments (NextJS, Gatsby)
19 | * Compatible to hook into existing Intercom instance (loaded by [Segment](https://segment.com/))
20 |
21 | ## Installation
22 |
23 | ```sh
24 | # pnpm
25 | pnpm add react-use-intercom
26 |
27 | # npm
28 | npm install react-use-intercom
29 |
30 | # yarn
31 | yarn add react-use-intercom
32 | ```
33 |
34 | ## Quickstart
35 |
36 | ```ts
37 | import * as React from 'react';
38 |
39 | import { IntercomProvider, useIntercom } from 'react-use-intercom';
40 |
41 | const INTERCOM_APP_ID = 'your-intercom-app-id';
42 |
43 | const App = () => (
44 |
45 |
46 |
47 | );
48 |
49 | // Anywhere in your app
50 | const HomePage = () => {
51 | const { boot, shutdown, hide, show, update } = useIntercom();
52 |
53 | return Boot intercom! ☎️ ;
54 | };
55 | ```
56 |
57 | ## Context
58 | This library is a React abstraction of [IntercomJS](https://developers.intercom.com/installing-intercom/docs/intercom-for-web). `react-use-intercom` tries to keep as close as a one-on-one abstraction of the "vanilla" Intercom functionality.
59 |
60 | Note that many issues could be related to the vanilla IntercomJS library. Please see https://community.intercom.com/ before reporting an issue here.
61 |
62 | ## Links
63 | * [API](#api)
64 | * [Playground](#playground)
65 | * [Examples](#examples)
66 | * [TypeScript](#typescript)
67 | * [Troubleshoot](#troubleshoot)
68 | * [Advanced](#advanced)
69 |
70 | ## API
71 | * [IntercomProvider](#intercomprovider)
72 | * [useIntercom](#useintercom)
73 | * [IntercomProps](#intercomprops)
74 |
75 | ### IntercomProvider
76 | `IntercomProvider` is used to initialize the `window.Intercom` instance. It makes sure the initialization is only done once. If any listeners are passed, the `IntercomProvider` will make sure these are attached.
77 |
78 | Place the `IntercomProvider` as high as possible in your application. This will make sure you can call `useIntercom` anywhere.
79 |
80 | #### Props
81 | | name | type | description | required | default |
82 | |---------------------|------------------|-----------------------------------------------------------------------------------------|----------|---------|
83 | | appId | string | app ID of your Intercom instance | true | |
84 | | children | React.ReactNode | React children | true | |
85 | | autoBoot | boolean | indicates if Intercom should be automatically booted. If `true` no need to call `boot`, the `IntercomProvider` will call it for you | false | false |
86 | | onHide | () => void | triggered when the Messenger hides | false | |
87 | | onShow | () => void | triggered when the Messenger shows | false | |
88 | | onUnreadCountChange | (number) => void | triggered when the current number of unread messages changes | false | |
89 | | onUserEmailSupplied | () => void | triggered when a visitor enters their email into the Messenger | false | |
90 | | shouldInitialize | boolean | indicates if the Intercom should be initialized. Can be used in multistaged environment | false | true |
91 | | apiBase | string | If you need to route your Messenger requests through a different endpoint than the default. Generally speaking, this is not needed. Format: `https://${INTERCOM_APP_ID}.intercom-messenger.com` (See: [https://github.com/devrnt/react-use-intercom/pull/96](https://github.com/devrnt/react-use-intercom/pull/96)) | false | |
92 | | initializeDelay | number | Indicates if the intercom initialization should be delayed, delay is in ms, defaults to 0. See https://github.com/devrnt/react-use-intercom/pull/236 | false | |
93 | | autoBootProps | IntercomProps | Pass properties to `boot` method when `autoBoot` is `true` | false | |
94 |
95 | #### Example
96 | ```ts
97 | const App = () => {
98 | const [unreadMessagesCount, setUnreadMessagesCount] = React.useState(0);
99 |
100 | const onHide = () => console.log('Intercom did hide the Messenger');
101 | const onShow = () => console.log('Intercom did show the Messenger');
102 | const onUnreadCountChange = (amount: number) => {
103 | console.log('Intercom has a new unread message');
104 | setUnreadMessagesCount(amount);
105 | };
106 | const onUserEmailSupplied = () => {
107 | console.log('Visitor has entered email');
108 | };
109 |
110 | return (
111 |
119 | Hi there, I am a child of the IntercomProvider
120 |
121 | );
122 | };
123 | ```
124 |
125 | ### useIntercom
126 | Used to retrieve all methods bundled with Intercom. These are based on the official [Intercom docs](https://developers.intercom.com/installing-intercom/docs/javascript-api-attributes-objects). Some extra methods were added to improve convenience.
127 |
128 | Make sure `IntercomProvider` is wrapped around your component when calling `useIntercom()`.
129 |
130 | **Remark** - You can't use `useIntercom()` in the same component where `IntercomProvider` is initialized.
131 |
132 | #### API
133 |
134 | | name | type | description |
135 | |-----------------|--------------------------------------------|-------------------------------------------------------------------------------------------------------------------------------------|
136 | | isOpen | boolean | the visibility status of the messenger |
137 | | boot | (props?: IntercomProps) => void | boots the Intercom instance, not needed if `autoBoot` in `IntercomProvider` is `true` |
138 | | shutdown | () => void | shuts down the Intercom instance |
139 | | hardShutdown | () => void | same functionality as `shutdown`, but makes sure the Intercom cookies, `window.Intercom` and `window.intercomSettings` are removed. |
140 | | update | (props?: IntercomProps) => void | updates the Intercom instance with the supplied props. To initiate a 'ping', call `update` without props |
141 | | hide | () => void | hides the Messenger, will call `onHide` if supplied to `IntercomProvider` |
142 | | show | () => void | shows the Messenger, will call `onShow` if supplied to `IntercomProvider` |
143 | | showMessages | () => void | shows the Messenger with the message list |
144 | | showNewMessage | (content?: string) => void | shows the Messenger as if a new conversation was just created. If `content` is passed, it will fill in the message composer |
145 | | getVisitorId | () => string | gets the visitor id |
146 | | startTour | (tourId: number) => void | starts a tour based on the `tourId` |
147 | | startChecklist | (checklistId: number) => void | starts a checklist based on the `checklistId` |
148 | | trackEvent | (event: string, metaData?: object) => void | submits an `event` with optional `metaData`
149 | | showArticle | (articleId: string) => void | opens the Messenger with the specified article by `articleId`
150 | | startSurvey | (surveyId: number) => void | Trigger a survey in the Messenger by `surveyId`
151 | | showSpace | (spaceName: IntercomSpace) => void | Opens the Messenger with the specified space
152 | | showTicket | (ticketId: number) => void | Opens the Messenger with the specified ticket by `ticketId`
153 | | showConversation | (conversationId: number) => void | Opens the Messenger with the specified conversation by `conversationId`
154 |
155 | #### Example
156 | ```ts
157 | import * as React from 'react';
158 |
159 | import { IntercomProvider, useIntercom } from 'react-use-intercom';
160 |
161 | const INTERCOM_APP_ID = 'your-intercom-app-id';
162 |
163 | const App = () => (
164 |
165 |
166 |
167 | );
168 |
169 | const HomePage = () => {
170 | const {
171 | boot,
172 | shutdown,
173 | hardShutdown,
174 | update,
175 | hide,
176 | show,
177 | showMessages,
178 | showNewMessage,
179 | getVisitorId,
180 | startTour,
181 | startChecklist,
182 | trackEvent,
183 | showArticle,
184 | startSurvey,
185 | showSpace,
186 | showTicket,
187 | showConversation
188 | } = useIntercom();
189 |
190 | const bootWithProps = () => boot({ name: 'Russo' });
191 | const updateWithProps = () => update({ name: 'Ossur' });
192 | const handleNewMessages = () => showNewMessage();
193 | const handleNewMessagesWithContent = () => showNewMessage('content');
194 | const handleGetVisitorId = () => console.log(getVisitorId());
195 | const handleStartTour = () => startTour(123);
196 | const handleStartChecklist = () => startChecklist(456);
197 | const handleTrackEvent = () => trackEvent('invited-friend');
198 | const handleTrackEventWithMetaData = () =>
199 | trackEvent('invited-frind', {
200 | name: 'Russo',
201 | });
202 | const handleShowArticle = () => showArticle(123456);
203 | const handleStartSurvey = () => startSurvey(123456);
204 | const handleShowSpace = () => showSpace('tasks');
205 | const handleShowTicket = () => showTicket(123);
206 | const handleShowConversation = () => showConversation(123);
207 |
208 | return (
209 | <>
210 | Boot intercom
211 | Boot with props
212 | Shutdown
213 | Hard shutdown
214 | Update clean session
215 | Update session with props
216 | Show messages
217 | Hide messages
218 | Show message list
219 | Show new messages
220 |
221 | Show new message with pre-filled content
222 |
223 | Get visitor id
224 | Start tour
225 | Start checklist
226 | Track event
227 |
228 | Track event with metadata
229 |
230 | Open article in Messenger
231 | Start survey in Messenger
232 | Open space in Messenger
233 | Open ticket in Messenger
234 | Open conversation in Messenger
235 | >
236 | );
237 | };
238 | ```
239 | ### IntercomProps
240 | All the Intercom default attributes/props are camel cased (`appId` instead of `app_id`) in `react-use-intercom`, see [IntercomProps](https://github.com/devrnt/react-use-intercom/blob/main/packages/react-use-intercom/src/types.ts#L257) to see what attributes you can pass to `boot` or `update`. Or check the Intercom [docs](https://developers.intercom.com/installing-intercom/docs/javascript-api-attributes-objects)
241 | to see all the available attributes/props.
242 |
243 | **Remark** - all the listed Intercom attributes [here](https://developers.intercom.com/installing-intercom/docs/javascript-api-attributes-objects) are snake cased, in `react-use-intercom` these are camel cased.
244 |
245 | #### Custom attributes
246 | Still want to pass custom attributes to Intercom? Whether `boot` or `update` is used, you can add your custom properties by passing these through `customAttributes` in the `boot` or `update` method.
247 |
248 | **Remark** - the keys of the `customAttributes` object should be snake cased (this is how Intercom wants them). They are rawly passed to Intercom.
249 | ```ts
250 | const { boot } = useIntercom();
251 |
252 | boot({
253 | name: 'Russo',
254 | customAttributes: { custom_attribute_key: 'hi there' },
255 | })
256 | ```
257 |
258 | ## Playground
259 | Small playground to showcase the functionalities of `react-use-intercom`.
260 |
261 | ### useIntercom
262 | [https://devrnt.github.io/react-use-intercom/#/useIntercom](https://devrnt.github.io/react-use-intercom/#/useIntercom)
263 |
264 | ### useIntercom (with Intercom tour)
265 | [https://devrnt.github.io/react-use-intercom/#/useIntercomTour](https://devrnt.github.io/react-use-intercom/#/useIntercomTour)
266 |
267 | ## Examples
268 | Go to [examples](https://github.com/devrnt/react-use-intercom/tree/main/apps/examples) to check out some integrations (Gatsby, NextJS...).
269 |
270 | ## TypeScript
271 | All the possible pre-defined options to pass to the Intercom instance are typed. So whenever you have to pass [IntercomProps](src/types.ts), all the possible properties will be available out of the box.
272 | These props are `JavaScript` 'friendly', so [camelCase](https://en.wikipedia.org/wiki/Camel_case). No need to pass the props with [snake_cased](https://en.wikipedia.org/wiki/Snake_case) keys.
273 |
274 | **Remark** - if you want to pass custom properties, you should still use [snake_cased](https://en.wikipedia.org/wiki/Snake_case) keys.
275 |
276 |
277 | ## Troubleshoot
278 | * I'm seeing `Please wrap your component with IntercomProvider` in the console.
279 | > Make sure `IntercomProvider` is initialized before calling `useIntercom()`. You only need to initialize `IntercomProvider` once. It is advised to initialize `IntercomProvider` as high as possible in your application tree.
280 |
281 | > Make sure you aren't calling `useIntercom()` in the same component where you initialized `IntercomProvider`.
282 |
283 | * I'm seeing `Some invalid props were passed to IntercomProvider. Please check following props: [properties]` in the console.
284 | > Make sure you're passing the correct properties to the `IntercomProvider`. Check [IntercomProvider](#intercomprovider) to see all the properties.
285 | > Mind that all the properties in `react-use-intercom` are camel cased, except for the `customAttributes` property in the `boot` and `update` method from `useIntercom`.
286 |
287 | ## Advanced
288 |
289 | ### Delay initialization
290 |
291 | ` ` uses an official intercom snippet and is directly initialized on load. In the background this snippet will load some external code that makes Intercom work. All of this magic happens on the initial load and in some use cases this can become problematic (E.g. when LCP is priority).
292 |
293 | Since [v1.2.0](https://github.com/devrnt/react-use-intercom/releases/tag/v1.2.0) it's possible to delay this initialisation by passing `initializeDelay` in ` ` (it's in milliseconds). However most of the users won't need to mess with this.
294 |
295 | For reference see https://github.com/devrnt/react-use-intercom/pull/236 and https://forum.intercom.com/s/question/0D52G00004WxWLs/can-i-delay-loading-intercom-on-my-site-to-reduce-the-js-load
296 |
297 | ## Contributing
298 |
299 | Contributions are welcome! Please read the [Contributing Guidelines](CONTRIBUTING.md) for details on how to contribute to the project.
300 |
--------------------------------------------------------------------------------
/packages/react-use-intercom/jest.config.cjs:
--------------------------------------------------------------------------------
1 | /** @type {import('ts-jest').JestConfigWithTsJest} */
2 | module.exports = {
3 | preset: 'ts-jest',
4 | testEnvironment: 'jest-environment-jsdom',
5 | moduleFileExtensions: ['ts', 'tsx', 'js', 'jsx', 'json'],
6 | collectCoverageFrom: ['test/**/*.{ts,tsx,js,jsx}'],
7 | testRegex: '(/tests/jest/.*|(\\.|/)(test|spec))\\.(ts|tsx)$',
8 | };
9 |
--------------------------------------------------------------------------------
/packages/react-use-intercom/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "react-use-intercom",
3 | "author": "devrnt",
4 | "main": "dist/index.cjs",
5 | "module": "dist/index.js",
6 | "description": "React Intercom integration without the hassle, driven by hooks.",
7 | "homepage": "https://github.com/devrnt/react-use-intercom#readme",
8 | "version": "5.4.3",
9 | "license": "MIT",
10 | "types": "dist/index.d.ts",
11 | "sideEffects": false,
12 | "type": "module",
13 | "repository": {
14 | "type": "git",
15 | "url": "git+https://github.com/devrnt/react-use-intercom.git"
16 | },
17 | "bugs": {
18 | "url": "https://github.com/devrnt/react-use-intercom/issues"
19 | },
20 | "files": [
21 | "dist"
22 | ],
23 | "exports": {
24 | ".": {
25 | "import": {
26 | "types": "./dist/index.d.ts",
27 | "default": "./dist/index.js"
28 | },
29 | "require": {
30 | "types": "./dist/index.d.cts",
31 | "default": "./dist/index.cjs"
32 | }
33 | }
34 | },
35 | "keywords": [
36 | "react",
37 | "reactjs",
38 | "intercom",
39 | "intercomjs",
40 | "react-intercom",
41 | "intercom-react",
42 | "typescript",
43 | "react-hooks",
44 | "react-use-intercom",
45 | "nextjs",
46 | "gatsby"
47 | ],
48 | "scripts": {
49 | "dev": "tsup src/index.ts --format esm,cjs --sourcemap --watch --dts --external react",
50 | "build": "tsup",
51 | "test": "jest test",
52 | "test:watch": "jest --watch",
53 | "test:coverage": "jest --coverage",
54 | "lint": "eslint src test",
55 | "lint:fix": "eslint --fix",
56 | "bundlesize": "pnpm build && size-limit",
57 | "publint": "publint"
58 | },
59 | "peerDependencies": {
60 | "react": ">=16.8.0",
61 | "react-dom": ">=16.8.0"
62 | },
63 | "size-limit": [
64 | {
65 | "path": "./dist/index.cjs",
66 | "limit": "3 kB"
67 | },
68 | {
69 | "path": "./dist/index.js",
70 | "limit": "3 kB"
71 | }
72 | ],
73 | "publishConfig": {
74 | "registry": "https://registry.npmjs.org"
75 | },
76 | "devDependencies": {
77 | "@size-limit/preset-small-lib": "^7.0.3",
78 | "@testing-library/react": "^14.0.0",
79 | "@types/jest": "^27.5.2",
80 | "@types/node": "^18.11.19",
81 | "@types/react": "^18.2.14",
82 | "@types/react-dom": "^18.2.6",
83 | "eslint-plugin-simple-import-sort": "^10.0.0",
84 | "jest": "^29.4.1",
85 | "jest-environment-jsdom": "^29.4.1",
86 | "publint": "^0.3.4",
87 | "react": "^18.2.0",
88 | "react-dom": "^18.2.0",
89 | "react-test-renderer": "^18.2.0",
90 | "size-limit": "^7.0.3",
91 | "source-map": "0.6.1",
92 | "ts-jest": "^29.0.5",
93 | "tslib": "^2.8.1",
94 | "tsup": "^8.3.6",
95 | "typescript": "^5.7.3"
96 | }
97 | }
--------------------------------------------------------------------------------
/packages/react-use-intercom/src/api.ts:
--------------------------------------------------------------------------------
1 | import * as logger from './logger';
2 | import { IntercomMethod } from './types';
3 | import { isSSR } from './utils';
4 |
5 | /**
6 | * Safely exposes `window.Intercom` and passes the arguments to the instance
7 | *
8 | * @param method method passed to the `window.Intercom` instance
9 | * @param args arguments passed to the `window.Intercom` instance
10 | *
11 | * @see {@link https://developers.intercom.com/installing-intercom/docs/intercom-javascript}
12 | */
13 | const IntercomAPI = (method: IntercomMethod, ...args: Array) => {
14 | if (!isSSR && window.Intercom) {
15 | return window.Intercom.apply(null, [method, ...args]);
16 | } else {
17 | logger.log('error', `${method} Intercom instance is not initalized yet`);
18 | }
19 | };
20 |
21 | export default IntercomAPI;
22 |
--------------------------------------------------------------------------------
/packages/react-use-intercom/src/context.ts:
--------------------------------------------------------------------------------
1 | import * as React from 'react';
2 |
3 | import { IntercomContextValues } from './types';
4 |
5 | const IntercomContext = React.createContext(
6 | undefined,
7 | );
8 |
9 | export default IntercomContext;
10 |
--------------------------------------------------------------------------------
/packages/react-use-intercom/src/global.d.ts:
--------------------------------------------------------------------------------
1 | export {};
2 | declare global {
3 | interface Window {
4 | Intercom: any;
5 | intercomSettings: any;
6 | attachEvent: any;
7 | }
8 | interface HTMLScriptElement {
9 | parentNode: any;
10 | }
11 | }
12 |
--------------------------------------------------------------------------------
/packages/react-use-intercom/src/index.ts:
--------------------------------------------------------------------------------
1 | export { IntercomProvider } from './provider';
2 | export { useIntercom } from './useIntercom';
3 | export * from './types';
4 |
--------------------------------------------------------------------------------
/packages/react-use-intercom/src/initialize.ts:
--------------------------------------------------------------------------------
1 | /* eslint-disable @typescript-eslint/ban-ts-comment */
2 | /* eslint-disable prefer-rest-params */
3 | /* eslint-disable no-var */
4 | // @ts-nocheck
5 | /**
6 | * Snippet to initialize the Intercom instance
7 | *
8 | * @param appId - Intercom app id
9 | * @param [timeout=0] - Amount of milliseconds that the initialization should be delayed, defaults to 0
10 | * @param [cspNonce=undefined] - Content-Security-Policy nonce to use for the Intercom