Using Office Javascript API in Next.js

Brian Tang 1 Reputation point
2022-11-01T02:11:57.027+00:00

I've developed a web app in Next.js and I'm trying to use the Office.js API, but it doesn't work and it keeps getting this error message:

Error was not caught TypeError: window.history[method] is not a function  
    at Router.changeState (router.js?8684:912:35)  
    at eval (router.js?8684:1564:26)  

I've tried importing the script by installing @microsoft/office-js and including

<Script   
    src="https://appsforoffice.microsoft.com/lib/1.1/hosted/office.js"   
    strategy='beforeInteractive'  
/>  

in the _app.js file using 'next/script'.

Can anyone help with this? I can't seem to find any documentation to point me in the right direction

Microsoft 365 and Office Development Other
0 comments No comments
{count} votes

4 answers

Sort by: Most helpful
  1. Nik Jmaeff 5 Reputation points
    2024-01-27T16:23:19.1433333+00:00

    I discovered that office.js does something to the window.history.replaceState function. To get things working with the nextjs dev server, I needed to create a _document.tsx file like the one below, which patches the method after importing office.js. This patch is not required for production as we are using the output: 'export' build option with nextjs. This was a quick fix - I'm unsure what edge cases may come up due to the patch.

    import { Head, Html, Main, NextScript } from 'next/document';
    
    export default function Document() {
      return (
        <Html lang='en'>
          <Head>
            <meta charSet="UTF-8"/>
            <meta httpEquiv="X-UA-Compatible" content="IE=Edge"/>
            {process.env.NODE_ENV !== 'production' &&
              <script dangerouslySetInnerHTML={{
                __html: `window._replaceState = window.replaceState`
              }}/>}
            <script type="text/javascript"
                    src="https://appsforoffice.microsoft.com/lib/1.1/hosted/office.js"></script>
            {process.env.NODE_ENV !== 'production' &&
              <script dangerouslySetInnerHTML={{
                __html: `window.history.replaceState = window._replaceState`
              }}
              />}
          </Head>
          <body>
          <Main/>
          <NextScript/>
          </body>
        </Html>
      );
    }
    
    
    1 person found this answer helpful.

  2. Elizabeth Samuel MSFT 11 Reputation points Microsoft Employee
    2022-11-04T19:36:58.747+00:00

    @Brian Tang I'm not familiar with Next.js but are you able to include the script entry under the head element?

    Referencing the Office JavaScript API library


  3. Corben Leo 1 Reputation point
    2022-12-05T15:11:04.877+00:00

    Hey @Brian Tang ,

    Did you have any luck figuring this out? I'm stuck on the same issue currently.

    0 comments No comments

  4. Saksham Goyal 0 Reputation points
    2024-01-30T05:59:16.0233333+00:00

    this is my _app.tsx:

    export default function App({ Component, pageProps } : any) : JSX.Element {
        const [loadedOffice, setLoadedOffice] = React.useState<boolean>(false);
        return (
                <Head>
                    <title>{'Office Plugin'}</title>
                    <meta
                        name='viewport'
                        content='minimum-scale=1, initial-scale=1, width=device-width, user-scalable=no'/>
                    <link rel='shortcut icon' href='/favicon.svg' />
                </Head>
                {process.env.NODE_ENV !== 'production' &&
                    <Script
                        id='beforeloadOffice'
                        dangerouslySetInnerHTML={{__html : 'window._replaceState = window.replaceState'}}/>
                }
                <Script
                    type='text/javascript'
                    src='https://appsforoffice.microsoft.com/lib/1.1/hosted/office.js'
                    crossOrigin='anonymous'
                    defer={false}
                    onLoad={() => setLoadedOffice(true)}/>
                {process.env.NODE_ENV !== 'production' && loadedOffice &&
                    <Script
                        id='afterloadOffice'
                        dangerouslySetInnerHTML={{__html : 'window.history.replaceState = window._replaceState'}}/>
                }
                <Component
                    {...pageProps}
                    loadedOffice={loadedOffice} />
        );
    }
    

    then in my taskpane.tsx:

    export default function Taskpane({loadedOffice} : Props) : JSX.Element {
        if (!loadedOffice) {return <div> {'Loading...'} </div>;}
    
        console.log('Office.js loaded');
    
        Office.onReady(() => {
            console.log('Office.js ready');
        });
    
        return (
            // ..... whatever now
                mt={50}
                justify='center'>
                <Button size='xl'>{'Welcome to Mantine!'}</Button>
            </Group>
        );
    }
    
    
    0 comments No comments

Your answer

Answers can be marked as Accepted Answers by the question author, which helps users to know the answer solved the author's problem.