How to Use a Javascript Reporting Tool with Next.js
Next.js is a React-based framework that provides a well-defined structure for your application and optimizations that make the development process and final application faster. Unlike classic front-end frameworks, such as Angular or Vue, Next.js supports Pre-Rendering of the application’s pages at runtime on the server-side or at building time.
ActiveReportsJS components need a browser environment and cannot work on the server-side or at the building time. However, it’s certainly possible to use ActiveReportsJS components within a Next.js application. This article describes the approach to getting it done.
Using Components Wrappers
Suppose you want to use ActiveReportsJS Report Viewer or Report Designer component in a Next.js application. Implementing a wrapper that encapsulates the component’s functionality and exposes an interface to consume it could be convenient. For example, here is the code of the Report Viewer’s wrapper that exposes, in addition to the built-in Viewer’s properties, the reportUri property that points to a report to be loaded.
import { Viewer } from "@grapecity/activereports-react";
import { Props as ViewerProps } from "@grapecity/activereports-react";
import { PdfExport } from "@grapecity/activereports";
import React from "react";
// import the default theme for the report viewer
import "@grapecity/activereports/styles/ar-js-ui.css";
import "@grapecity/activereports/styles/ar-js-viewer.css";
// eslint-disable-next-line
const pdf = PdfExport;
// eslint-disable-next-line react/display-name
const ViewerWrapper = (props: ViewerWrapperProps) => {
const ref = React.useRef<Viewer>(null);
React.useEffect(() => {
ref.current?.Viewer.open(props.reportUri);
}, [props.reportUri]);
return <Viewer {...props} ref={ref} />;
};
export type ViewerWrapperProps = ViewerProps & { reportUri: string };
export default ViewerWrapper;
Similarly, the wrapper for the Report Designer component could look like the following
import { Designer } from "@grapecity/activereports-react";
import { DesignerProps } from "@grapecity/activereports-react";
import React from "react";
import "@grapecity/activereports/styles/ar-js-ui.css";
import "@grapecity/activereports/styles/ar-js-designer.css";
// eslint-disable-next-line react/display-name
const DesignerWrapper = (props: DesignerWrapperProps) => {
const ref = React.useRef<Designer>(null);
React.useEffect(() => {
ref.current?.setReport({id: props.reportUri});
}, [props.reportUri]);
return <Designer {...props} ref={ref} />;
};
export type DesignerWrapperProps = DesignerProps & { reportUri: string };
export default DesignerWrapper;
Using Wrappers for API Invocation
Suppose you want to use the ActiveReportsJS API. In that case, it could also be encapsulated within a wrapper that does not render any UI but simply tracks properties' changes and calls the API functions. Here is an example of the PDFExport wrapper:
import {
PdfSettings,
exportDocument,
} from "@grapecity/activereports/pdfexport";
import { PageReport } from "@grapecity/activereports/core";
import React from "react";
export type PdfExportWrapperProps = PdfSettings & { reportUri: string };
async function exportReport(props: PdfExportWrapperProps) {
const pageReport = new PageReport();
await pageReport.load(props.reportUri);
const doc = await pageReport.run();
const res = await exportDocument(doc, props);
res.download("report.pdf");
}
export default function PdfExportWrapper(props: PdfExportWrapperProps) {
React.useEffect(() => {
if(props.reportUri?.length)
exportReport(props);
}, [props]);
return null;
}
Dynamic Loading of Wrappers
Next.js contains the Dynamic Import feature that allows loading components at runtime and prevents server-side rendering. This is required for using ActiveReportsJS components within a Next.js application. Below is the sample of the Next.js Page that displays the Report Viewer component and loads the Products.rdlx-json report into it. The code assumes the wrapper’s code is in the components\ReportViewer.tsx file.
import type { NextPage } from "next";
import React from "react";
import styles from "../styles/Home.module.css";
import { ViewerWrapperProps } from "../components/ReportViewer";
import dynamic from "next/dynamic";
const Viewer = dynamic<ViewerWrapperProps>(
async () => {
return (await import("../components/ReportViewer")).default;
},
{ ssr: false }
);
const Home: NextPage = () => {
return (
<div
className={styles.container}
style={{ width: "100%", height: "100vh" }}
>
<Viewer reportUri="report.rdlx-json" />
</div>
);
};
export default Home;
You could use the same approach for API wrappers:
import type { NextPage } from "next";
import React from "react";
import styles from "../styles/Home.module.css";
import { PdfExportWrapperProps } from "../components/PdfExport";
import dynamic from "next/dynamic";
const PdfExportWrapper = dynamic<PdfExportWrapperProps>(
async () => {
return (await import("../components/PdfExport")).default;
},
{ ssr: false }
);
const Home: NextPage = () => {
const [report, setReport] = React.useState<string>("");
return (
<div
className={styles.container}
style={{ width: "100%", height: "100vh" }}
>
<button onClick={() => setReport("report.rdlx-json")}>Export Report</button>
<PdfExportWrapper reportUri={report} pdfVersion="1.7" />
</div>
);
};
export default Home;