Before you start
- A React app with an
index.htmlof its own. With Vite, it's in the project root, next topackage.json, not inpublic. - Using Next.js? It has no
index.html. Follow the Next.js guide instead. - Your snippet from Websites in the Pidge inbox.
<script>window.$pidge = []; window.PIDGE_WEBSITE_ID = "YOUR-WEBSITE-ID";</script> <script src="https://app.pidge.chat/client/l.js" async></script>
Your own copy is in the Pidge inbox under Websites, in Code for your website, with your website ID already filled in. If you've set up a chat domain, it loads from there instead.
Add the snippet
- Open
index.htmlin the project root. - Paste the two lines just before
</body>, after the line that loads your app. Change the first line towindow.$pidge || [], so commands your components push early are kept.
<body>
<div id="root"></div>
<script type="module" src="/src/main.tsx"></script>
<script>window.$pidge = window.$pidge || []; window.PIDGE_WEBSITE_ID = "YOUR-WEBSITE-ID";</script>
<script src="https://app.pidge.chat/client/l.js" async></script>
</body>
- Run
npm run buildand deploy as usual. Vite copies both lines into the builtindex.htmlas they are.
The chat adds itself next to #root, not inside it, so React never renders over
it. Nothing in your components needs to change.
Page changes with React Router
You don't need to do anything. Pidge notices when the address changes, so
<Link> clicks and the back button keep the same chat, open or closed, and
the inbox shows which page the visitor is on. We checked this with
BrowserRouter: the conversation stayed open across pages and each route showed
up in the inbox.
Why not a useEffect?
You can load the script from a component, but it buys you nothing and adds two catches:
- In development,
<StrictMode>runs every effect twice, so a component that adds the script tag adds it twice. Pidge starts only once per page, so you still get one bubble, but it's confusing to debug. - A component inside one route only loads the chat when someone lands on that route. Removing the script tag when the component unmounts doesn't remove the chat either.
If you've already written one, replacing it with the two lines in index.html is the simpler fix.
Keep the website ID in an env file
Vite fills in %VITE_…% names in index.html, in development and in
the build. Put the ID in .env:
VITE_PIDGE_WEBSITE_ID=YOUR-WEBSITE-ID
and use it in the first line of the snippet:
<script>window.$pidge = window.$pidge || []; window.PIDGE_WEBSITE_ID = "%VITE_PIDGE_WEBSITE_ID%";</script>
If the variable isn't set where you build, Vite prints a warning and leaves
%VITE_PIDGE_WEBSITE_ID% in the page as written, and the chat won't start. Check
the build log, or view the page source, if the bubble is missing after a deploy.
Open the chat from your own components
Push commands to window.$pidge from an event handler. In TypeScript, declare it once:
declare global {
interface Window {
$pidge?: { push: (...commands: unknown[]) => void } | unknown[];
}
}
export function ChatButton() {
return (
<button
type="button"
onClick={() => {
window.$pidge = window.$pidge || [];
window.$pidge.push(["do", "chat:open"]);
}}
>
Chat with us
</button>
);
}
To pass a logged-in customer's name, push it from an effect:
useEffect(() => {
window.$pidge = window.$pidge || [];
window.$pidge.push(["set", "user:nickname", [user.name]]);
}, [user.name]);
StrictMode sends it twice in development, which does no harm: the second one sets the same
name. If the script hasn't loaded yet, the command waits in the array and runs when it has.
For emails your team can trust, sign them on the server: see
signed visitor emails. Push
["do", "session:reset"] when someone logs out. The
widget commands list everything else you can push.
List your domains in Pidge
The chat only connects on domains you've listed for the website. In the inbox, open
Websites and put each domain on its own line in
Domains. Subdomains are included, so example.com also
covers www.example.com.
If your host gives the app a second address, like yourapp.netlify.app or
yourapp.vercel.app, list that full address too if people visit it there.
To try the chat while you develop, add localhost to the list too. Pidge
matches the host name, so any port works, including the localhost:5173 that
Vite prints. 127.0.0.1 isn't accepted, so open your dev server at
localhost.
Check that it works
- Open your site in a private window, so you're seeing what a visitor sees.
- The chat bubble should be in the bottom corner. Send yourself a message and answer it from the inbox.
- Click through two or three pages and check the chat is still there.
- In the inbox, the website's Not installed yet label goes away after the first visit from a listed domain.
If the bubble shows but the chat won't connect, the domain you're on isn't in the website's list in Pidge. If there's no bubble at all, the snippet isn't on the page, or your host is still serving an older build.
Vite's own guide: vite.dev/guide/env-and-mode. React on StrictMode: react.dev/reference/react/StrictMode
Other platforms
- WordPress
- Shopify
- Squarespace
- Wix
- Webflow
- Ghost
- Framer
- Next.js
- Astro
- Vue and Nuxt
- Gatsby
- Hugo
- Jekyll
- Laravel
- Django
- Carrd
- Plain HTML
Stuck, or did a step look different on your screen? Tell us in the chat on this page.