Throwing "ReferenceError: window is not defined" on Next.js with SSR
The library fails to work with the Next.js framework ("next": "^12.1.6") while SSR.
The issue is caused by the missing window object within the file node_modules/@twa-dev/sdk/dist/sdk.js, and it occurs at the time of import. Ref: https://github.com/twa-dev/SDK/blob/master/src/sdk.ts
Transpiled file
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.WebApp = void 0;
require("./telegram-web-apps");
var telegramWindow = window; // Seems to be the line causing the problem
exports.WebApp = telegramWindow.Telegram.WebApp;
//# sourceMappingURL=sdk.js.map
Demo app:
import WebApp from '@twa-dev/sdk'; // Happens at the time of import
const MiniApp = () => {
return (
<div>
...
<button onClick={() => WebApp.showAlert(`Hello World!`)}>
Show Alert
</button>
</div>
);
};
export default MiniApp;
Console output
Uncaught ReferenceError: window is not defined
at <unknown> (file:///Users/ivan/code/communa/frontend/node_modules/@twa-dev/sdk/dist/telegram-web-apps.js:248:5)
at Object.<anonymous> (file:///Users/ivan/code/communa/frontend/node_modules/@twa-dev/sdk/dist/telegram-web-apps.js:274:3)
at Module._compile (node:internal/modules/cjs/loader:1364:14)
at Module._extensions..js (node:internal/modules/cjs/loader:1422:10)
at Module.load (node:internal/modules/cjs/loader:1203:32)
at Module._load (node:internal/modules/cjs/loader:1019:12)
at Module.require (node:internal/modules/cjs/loader:1231:19)
at require (node:internal/modules/helpers:177:18)
at Object.<anonymous> (file:///Users/ivan/code/communa/frontend/node_modules/@twa-dev/sdk/dist/sdk.js:4:1)
at Module._compile (node:internal/modules/cjs/loader:1364:14)
@ivanproskuryakov I am experiencing the same, let me know if you find a good fix.
I am able to use Next's Script tag to load the TWA script and then do the JS there, but I would like to use this SDK.
@kevcube Sure, I'll open a PR with a patch. Meanwhile, can you post your workaround for this error, please?
@ivanproskuryakov
"use client";
import Script from "next/script";
import { Telegram } from "@twa-dev/types";
declare global {
interface Window {
Telegram: Telegram;
}
}
export default function Page() {
return (
<Script
id="TelegramWebApp"
src="https://telegram.org/js/telegram-web-app.js"
onReady={() => {
window.Telegram.WebApp.MainButton.setParams({
text: `Hello`,
is_visible: true,
});
}}
/>
);
}
I'm using Next 14, this can go in layout or other imported files to auto load on all pages of your app.
@ivanproskuryakov
"use client"; import Script from "next/script"; import { Telegram } from "@twa-dev/types"; declare global { interface Window { Telegram: Telegram; } } export default function Page() { return ( <Script id="TelegramWebApp" src="https://telegram.org/js/telegram-web-app.js" onReady={() => { window.Telegram.WebApp.MainButton.setParams({ text: `Hello`, is_visible: true, }); }} /> ); }I'm using Next 14, this can go in
layoutor other imported files to auto load on all pages of your app.
It didn't work for me. Anyone faced the issue and find a solution other than this?
@ivanproskuryakov
"use client";
import Script from "next/script";
import { Telegram } from "@twa-dev/types";
declare global {
interface Window {
Telegram: Telegram;}
}
export default function Page() {
return (
<Scriptid="TelegramWebApp"src="https://telegram.org/js/telegram-web-app.js"onReady={() => {window.Telegram.WebApp.MainButton.setParams({text: `Hello`,is_visible: true,});}}/>);
}
I'm using Next 14, this can go in
layoutor other imported files to auto load on all pages of your app.It didn't work for me. Anyone faced the issue and find a solution other than this?
Just use telegram-mini-apps/twa.js and join @devs on telegram
Just check if window is exist, like this:
if (typeof window !== "undefined") {
// Your code here
}
something like this:
import WebApp from '@twa-dev/sdk'; // Happens at the time of import
const MiniApp = () => {
const onClick = () => {
if (typeof window !== "undefined") {
WebApp.showAlert(`Hello World!`)
}
}
return (
<div>
...
<button onClick={onClick}>
Show Alert
</button>
</div>
);
};
export default MiniApp;
still the same issue i faced. No PR with fix merged ?
The issue is still there!
Still same +
I meet this issue, and currently the workaround I used is to dynamic import the component which use import WebApp from '@twa/sdk'.
e.g:
import WebApp from "@twa-dev/sdk";
const Foo = () => {
const foo = () => {WebApp....}
}
export default Foo;
dynamic import the component:
import dynamic from 'next/dynamic';
import Foo from "@/components/foo";
const Foo = dynamic(() => import("foo"), { ssr: false });
const Page = () => {
return (
<div>
<Foo/>
</div>
)
}
I meet this issue, and currently the workaround I used is to dynamic import the component which use
import WebApp from '@twa/sdk'. e.g:import WebApp from "@twa-dev/sdk"; const Foo = () => { const foo = () => {WebApp....} } export default Foo;dynamic import the component:
import dynamic from 'next/dynamic'; import Foo from "@/components/foo"; const Foo = dynamic(() => import("foo"), { ssr: false }); const Page = () => { return ( <div> <Foo/> </div> ) }
You can use @telegram-apps/sdk-react without any problem.
I meet this issue, and currently the workaround I used is to dynamic import the component which use
import WebApp from '@twa/sdk'. e.g:import WebApp from "@twa-dev/sdk"; const Foo = () => { const foo = () => {WebApp....} } export default Foo;dynamic import the component:
import dynamic from 'next/dynamic'; import Foo from "@/components/foo"; const Foo = dynamic(() => import("foo"), { ssr: false }); const Page = () => { return ( <div> <Foo/> </div> ) }You can use @telegram-apps/sdk-react without any problem.
But some methods do not work with @telegram-apps/sdk-react
I was having the same issue but with client component, wich as supposed to work, the problem is that next apparently import the packages as ssr by default as you can see in the base errors in the screenshot "at (ssr)"
then I just imported the library this way, as I noted the issue on the error was that next was rendering the library at the server sidem then i came to the library and it was trying to access window, so it was nextjs fault, and this import worked to me
let WebApp: any;
if (typeof window !== "undefined") {
WebApp = require("@twa-dev/sdk").default;
}
Full code provider that I was using after the changes
"use client";
import {
createContext,
useContext,
useEffect,
useMemo,
useState,
ReactNode,
} from "react";
import crypto from "crypto";
let WebApp: any;
if (typeof window !== "undefined") {
WebApp = require("@twa-dev/sdk").default;
}
export interface ITelegramContext {
isOnTelegram: boolean;
}
const TelegramContext = createContext<ITelegramContext>({
isOnTelegram: false,
});
export const TelegramProvider: React.FC<{ children: ReactNode }> = ({
children,
}) => {
const [isOnTelegram, setIsOnTelegram] = useState(false);
const [isValid, setIsValid] = useState("invalid");
const [initDataUnsafe, setInitDataUnsafe] = useState<any>();
const [initData, setInitData] = useState<any>();
//Make a api url for this function its not supposed to be here
const verifyInitData = (telegramInitData: string): boolean => {
const urlParams = new URLSearchParams(telegramInitData);
const hash = urlParams.get('hash');
urlParams.delete('hash');
urlParams.sort();
let dataCheckString = '';
for (const [key, value] of urlParams.entries()) {
dataCheckString += `${key}=${value}\n`;
}
dataCheckString = dataCheckString.slice(0, -1);
const secret = crypto.createHmac('sha256', 'WebAppData').update("bot_key");
const calculatedHash = crypto.createHmac('sha256', secret.digest()).update(dataCheckString).digest('hex');
return calculatedHash === hash;
}
useEffect(() => {
if (WebApp.initDataUnsafe.user) {
setIsOnTelegram(true);
setInitDataUnsafe(JSON.stringify(WebApp.initDataUnsafe.user));
const { hash, ...rest } = WebApp.initDataUnsafe.user;
}
if (WebApp?.initData) {
setIsOnTelegram(true);
setInitData(JSON.stringify(WebApp.initData));
setIsValid(verifyInitData(WebApp.initData) ? "valid" : "invalid");
}
}, []);
const value = useMemo(
() => ({ isOnTelegram }),
[isOnTelegram],
);
return (
<TelegramContext.Provider value={value}>
IsValid
{isValid}
<br />
Unsafe
{initDataUnsafe}
<br />
Data
{initData}
{children}
</TelegramContext.Provider>
);
};
export const useTelegram = (): ITelegramContext => useContext(TelegramContext);