【NextJS】View and Download PDF

1. 概要

前回はスマホからローカル環境にアクセスする内容についてでした。今回はPDFを表示&ダウンロードする内容になります。

対象としては開発を1年程やってて自分で最初から開発してみたい方になります。そのため細かい用語などの説明はしません。

2. nodeのインストール

こちらを参考

3. プロジェクトを作成

3-1-1. こちらを参考

4. 必要なライブラリをインストール

4-1-1. こちらを参考

npm install @react-pdf/renderer --save

4-1-2. Requirements

You should upgrade to Next.js 14.1.1 or later.

5. ソースコード

※前回より差分のみを記載

5-1-1. src/app/components/component11/my-document.tsx

import { Document, Page, StyleSheet, View, Text } from "@react-pdf/renderer";

const styles = StyleSheet.create({
  page: {
    flexDirection: "row",
    backgroundColor: "#E4E4E4",
  },
  section: {
    margin: 10,
    padding: 10,
    flexGrow: 1,
  },
});

const MyDocument = () => {
  return (
    <Document>
      <Page size={"A4"} style={styles.page}>
        <View style={styles.section}>
          <Text>Section #1</Text>
        </View>
        <View style={styles.section}>
          <Text>Section #2</Text>
        </View>
      </Page>
    </Document>
  );
};

export default MyDocument;

5-1-2. src/app/components/component11/view-pdf.tsx

import { useEffect, useState } from "react";
import Box from "@mui/material/Box";
import Modal from "@mui/material/Modal";
import { PDFViewer } from "@react-pdf/renderer";
import MyDocument from "./my-document";

const style = {
  position: "absolute",
  top: "50%",
  left: "50%",
  transform: "translate(-50%, -50%)",
  width: "80vh",
  height: "60vh",
  bgcolor: "background.paper",
  border: "2px solid #000",
  boxShadow: 24,
  p: 4,
};

type Props = {
  name: string;
  open: boolean;
  setOpen: Function;
};

const ViewPDF = (props: Props) => {
  const [loaded, setLoaded] = useState<boolean>(false);

  useEffect(() => {
    setLoaded(true);
  }, []);

  const handleClose = () => props.setOpen(false);

  return (
    <div style={{ height: "100vh" }}>
      <Modal
        open={props.open}
        onClose={handleClose}
        aria-labelledby="modal-modal-title"
        aria-describedby="modal-modal-description"
      >
        <Box sx={style}>
          {loaded && (
            <PDFViewer showToolbar={true} width={"100%"} height={"100%"}>
              <MyDocument />
            </PDFViewer>
          )}
        </Box>
      </Modal>
    </div>
  );
};

export default ViewPDF;

5-1-3. src/app/components/component11/download-pdf.tsx

import { useEffect, useState } from "react";
import Button from "@mui/material/Button";
import { PDFDownloadLink } from "@react-pdf/renderer";
import MyDocument from "./my-document";

type Props = {
  fileName: string;
};

const DownloadPDF = (props: Props) => {
  const [loaded, setLoaded] = useState<boolean>(false);

  useEffect(() => {
    setLoaded(true);
  }, []);
  return (
    <div>
      {loaded && (
        <PDFDownloadLink document={<MyDocument />} fileName={props.fileName}>
          {({ blob, url, loading, error }) =>
            loading ? (
              "Loading document..."
            ) : (
              <Button variant={"contained"} sx={{ width: 200 }}>
                Download PDF
              </Button>
            )
          }
        </PDFDownloadLink>
      )}
    </div>
  );
};

export default DownloadPDF;

5-1-4. src/app/components/component11/client-page.tsx

"use client";

import { useState } from "react";
import { Button, Stack } from "@mui/material";
import ViewPDF from "./view-pdf";
import DownloadPDF from "./download-pdf";

const ClientPage = () => {
  const [open, setOpen] = useState(false);

  return (
    <>
      <Stack spacing={2} direction={"column"} textAlign={"center"}>
        <div>
          <Button
            variant={"contained"}
            sx={{ width: 200 }}
            onClick={() => setOpen(true)}
          >
            View PDF
          </Button>
        </div>
        <DownloadPDF fileName={"somename.pdf"} />
      </Stack>
      <ViewPDF name={"pdf"} open={open} setOpen={setOpen} />
    </>
  );
};

export default ClientPage;

5-1-5. src/app/components/component11/page.module.scss

.component {
  & ul {
    margin-left: 20px;
    & li {
      list-style: disc;
    }
  }
  & .calendar_area {
    text-align: center;
  }
}

5-1-6. src/app/components/component11/page.tsx

import Divider from "@mui/material/Divider";
import GoBack from "@/lib/components/go-back";
import scss from "./page.module.scss";
import ClientPage from "./client-page";

const Component11 = () => {
  return (
    <div className={scss.component}>
      <GoBack />
      <br />
      <br />
      <ul>
        <li>PDF</li>
        <ul>
          <li>表示</li>
          <li>生成</li>
        </ul>
      </ul>
      <Divider sx={{ marginTop: 2, marginBottom: 2 }} />
      <div className={scss.calendar_area}>
        <ClientPage />
      </div>
    </div>
  );
};

export default Component11;

5-1-7. src/app/components/page.module.scss

.components {
  color: blue;
  & ul {
    margin-left: 20px;
    & li {
      list-style: disc;
    }
  }
}

5-1-8. src/app/components/page.tsx

"use client";

import React from "react";
import { Link } from "@mui/material";

import scss from "./page.module.scss";

const Components = () => {
  return (
    <div className={scss.components}>
      <ul>
        <li>
          <Link href="/components/component01" underline="hover">
            Component01
          </Link>
        </li>
        <li>
          <Link href="/components/component02" underline="hover">
            Component02
          </Link>
        </li>
        <li>
          <Link href="/components/component03" underline="hover">
            Component03
          </Link>
        </li>
        <li>
          <Link href="/components/component04" underline="hover">
            Component04
          </Link>
        </li>
        <li>
          <Link href="/components/component05" underline="hover">
            Component05
          </Link>
        </li>
        <li>
          <Link href="/components/component06" underline="hover">
            Component06
          </Link>
        </li>
        <li>
          <Link href="/components/component07" underline="hover">
            Component07
          </Link>
        </li>
        <li>
          <Link href="/components/component08" underline="hover">
            Component08
          </Link>
        </li>
        <li>
          <Link href="/components/component09" underline="hover">
            Component09
          </Link>
        </li>
        <li>
          <Link href="/components/component10" underline="hover">
            Component10
          </Link>
        </li>
        <li>
          <Link href="/components/component11" underline="hover">
            Component11
          </Link>
        </li>
      </ul>
    </div>
  );
};

export default Components;

6. サーバーを起動

npm run dev

7. ブラウザで確認

  • http://localhost:3000

7-1-1. 表示

7-2-1. ダウンロード

8. ディレクトリの構造

省略

9. 備考

今回はPDFを表示&ダウンロードする内容についてでした。

10. 参考

投稿者プロフィール

Sondon
開発好きなシステムエンジニアです。
卓球にハマってます。

関連記事

  1. 【NextJS】Internationalization(i18n) …

  2. 【NextJS】ChatApp with Realtime updat…

  3. 【NextJS】Cognito with Amplify(Gen2)+…

  4. 【NextJS】FullCalendar

  5. 【NextJS】Cognito with Amplify(Gen2)+…

  6. 【NextJS】Hooks-useState(update separ…

最近の記事

制作実績一覧

  1. Checkeys