Hi, I'd like to display multiple pages of my PDF. I've followed the code sample on react-pdf. However, when I run the site on localhost, the PDF is installed rather than opened.
Please help
1. Error:
2. Folder structure:
3. What I've tried:
Include the media queries
Try all suggested file path (public, import, relative)
4. Here's my code:
import React from "react";
import { Page } from "react-pdf";
import { pdfjs } from "react-pdf";
import { Document } from "react-pdf/dist/esm/entry.webpack";
import Lesson_1 from "./Lesson 1.pdf";
// import {} from '../../../public/docs/pdf/Lesson 1.pdf'
// pdfjs.GlobalWorkerOptions.workerSrc = `https://cdnjs.cloudflare.com/ajax/libs/pdf.js/${pdfjs.version}/pdf.worker.js`;
const options = {
cMapUrl: "cmaps/",
cMapPacked: true,
};
function Lesson(props) {
const [numPages, setNumPages] = React.useState(null);
const [pageNumber, setPageNumber] = React.useState(1);
function onDocumentLoadSuccess({ numPages }) {
setNumPages(numPages);
}
return (
<div>
<Document
file="Lesson 1.pdf"
options={options}
onLoadSuccess={onDocumentLoadSuccess}
>
<Page pageNumber={pageNumber} />
</Document>
<p>
Page {pageNumber} of {numPages}
</p>
</div>
);
}
export default Lesson;
Related
How to get i18n working on Next 13 ?
I have created a nested [locale]/ folder in app/ but it just gives a 404
See my next.config.js
const nextConfig = {
experimental: {
appDir: true,
},
i18n: {
defaultLocale: 'fr,
locales: ['fr', 'en'],
localeDetection: true
}
}
Did you find a way to support i18n with React Server components ?
EDIT:
On the beta.nextjs doc it says :
We are currently not planning to include the following features in app:
Internationalization (i18n)
I have as well found an open issue about it, which does not provide any workaround yet.
Middleware is a solution to i18n in Next.js 13. Until there is an officially recommended way.
To use middleware in Next.js 13 with /app directory, you must create the /page directory (even if all your pages are going to be in /app directory) and a .keep file inside of it. Github issue about it
Create a dynamic route [locale] in the app directory and put all other routes there.
Set middleware so it redirects all traffic from routes without a locale param to ones that have it (f.e. based on Accept-language header or location of the client).
Get the value of the locale on a page from params.
A minimal middleware.ts content inspired by Eric Howey's article:
import { NextResponse } from "next/server";
import type { NextRequest } from "next/server";
// Regex to check whether something has an extension, e.g. .jpg
const PUBLIC_FILE = /\.(.*)$/;
export function middleware(request: NextRequest) {
const { nextUrl, headers } = request;
// Cloned url to work with
const url = nextUrl.clone();
// Client language, defaults to en
const language =
headers
.get("accept-language")
?.split(",")?.[0]
.split("-")?.[0]
.toLowerCase() || "en";
try {
// Early return if it is a public file such as an image or an api call
if (PUBLIC_FILE.test(nextUrl.pathname) || nextUrl.pathname.includes("/api")) {
return undefined;
}
// Proceed without redirection if on a localized path
if (
nextUrl.pathname.startsWith("/en") ||
nextUrl.pathname.startsWith("/de") ||
nextUrl.pathname.startsWith("/fr")
) {
return undefined;
}
if (language === "fr") {
url.pathname = `/fr${nextUrl.pathname}`;
return NextResponse.redirect(url);
}
if (language === "de") {
url.pathname = `/de${nextUrl.pathname}`;
return NextResponse.redirect(url);
}
if (!["de", "fr"].includes(language)) {
url.pathname = `/en${nextUrl.pathname}`;
return NextResponse.redirect(url);
}
return undefined;
} catch (error) {
console.log(error);
}
}
Even though i18n is no longer directly supported by Next.js in combination with the new app directory, there is still a way to solve this...
With the help of a middleware and with the use of i18next, react-i18next and i18next-resources-to-backend this is solvable.
It works on server side and on client side.
There's a little example showing how this could work directly by using i18next and react-i18next on server side and on client side.
...and here the corresponding blog post.
A snipped on how this could look like:
server side:
import Link from 'next/link'
import { useTranslation } from '../i18n'
import { Footer } from './components/Footer'
export default async function Page({ params: { lng } }) {
const { t } = await useTranslation(lng)
return (
<>
<h1>{t('title')}</h1>
<Link href={`/${lng}/second-page`}>
{t('to-second-page')}
</Link>
<br />
<Link href={`/${lng}/client-page`}>
{t('to-client-page')}
</Link>
<Footer lng={lng}/>
</>
)
}
client side:
'use client'
import Link from 'next/link'
import { useTranslation } from '../../i18n/client'
import { Footer } from '../components/Footer/client'
import { useState } from 'react'
export default function Page({ params: { lng } }) {
const { t } = useTranslation(lng, 'client-page')
const [counter, setCounter] = useState(0)
return (
<>
<h1>{t('title')}</h1>
<p>{t('counter', { count: counter })}</p>
<div>
<button onClick={() => setCounter(Math.max(0, counter - 1))}>-</button>
<button onClick={() => setCounter(Math.min(10, counter + 1))}>+</button>
</div>
<Link href={`/${lng}`}>
<button type="button">
{t('back-to-home')}
</button>
</Link>
<Footer lng={lng} />
</>
)
}
I just discovered PrismJs and it looks perfect. But for some reason, it doesn't highlight my code in following component :
import { useState, useEffect } from "react";
import Prism from "prismjs";
export default function EditCode() {
const [content, setContent] = useState("");
useEffect(() => {
Prism.highlightAll();
}, []);
useEffect(() => {
Prism.highlightAll();
}, [content]);
return (
<section className="codeGlobalContainer">
<textarea
className="codeInput"
value={content}
onChange={(e) => setContent(e.target.value)}
/>
<pre className="codeOutput">
<code className={`language-javascript`}>{content}</code>
</pre>
</section>
);
}
Is there anything missing to make it work ?
It's not specified on there npm page, but you need to download a themed CSS on there official site : PrismsJS
Then, you just move the CSS file to your directory and import it in your component as usual :
import "../../styles/prism.css";
as #FlowRan mentioned you need to import any theme you want to use
but
Note: you do not need to download the themes separately as they come with the package.
Import your theme in your file by using the import statement from-
'prismjs/themes/prism-{theme-name}.css';
I am creating a website where I have a pdf viewer inside my page imported dynamically.
the code runs locally without errors, but during the "npm run build" command I find the following error:
> Build error occurred
ReferenceError: window is not defined
at Object.<anonymous> (C:\Users\GustavoMorilla\MyProfile\node_modules\#pdftron\webviewer\webviewer.min.js:1:224)
at Object.g7Pv (C:\Users\Gustavo Morilla\MyProfile\.next\server\pages\resume\viewer.js:121:18)
at __webpack_require__ (C:\Users\Gustavo Morilla\MyProfile\.next\server\pages\resume\viewer.js:23:31)
at Module.vRHl (C:\Users\Gustavo Morilla\MyProfile\.next\server\pages\resume\viewer.js:134:76) {
type: 'ReferenceError'
info - Collecting page data .npm ERR! code ELIFECYCLE }
viewer.js code:
import React, { useRef, useEffect } from "react";
import WebViewer from "#pdftron/webviewer";
const Viewer = () => {
const viewer = useRef(null);
useEffect(() => {
WebViewer(
{
path: '/lib',
initialDoc: "/pdf/GustavoMorilla.pdf",
},
viewer.current
).then(function (instance) {
instance.setTheme("dark");
});
}, []);
return (
<div>
<div className="Viewer">
<div className="header flex justify-between">
<a>Resume</a>
<a className="text-sm">You can download the file on "Settings" button</a>
</div>
<div className="Webviewer" ref={viewer}></div>
</div>
</div>
);
};
export default Viewer;
resume.js code:
import SiteLayout from "../../components/SiteLayout";
import React from "react";
import dynamic from "next/dynamic";
const Viewer = dynamic(() => import("../resume/viewer.js"), { ssr: false });
export default function Resume({ resume }) {
return (
<div>
<SiteLayout>
<div className="main w-screen">
<Viewer />
</div>
</SiteLayout>
</div>
);
}
should be something related with the SSR or lifecycle... i dont know.
window object doesn't exist on the server side, use conditional rendering to solve the problem.
import SiteLayout from '../../components/SiteLayout'
import React from 'react'
import dynamic from 'next/dynamic'
const Viewer = dynamic(() => import('../resume/viewer.js'), { ssr: false })
export default function Resume({ resume }) {
return window ? (
<div>
<SiteLayout>
<div className="main w-screen">
<Viewer />
</div>
</SiteLayout>
</div>
) : null
}
I am trying to use vanta with next.js, following this guide. It works completely fine with the Net Effect, however, when I try to use the Globe Effect, I get
[VANTA] Init error TypeError: r.Geometry is not a constructor
at h.onInit (vanta.globe.min.js:1)
at h.init (vanta.globe.min.js:1)
at new r.VantaBase (vanta.globe.min.js:1)
at new h (vanta.globe.min.js:1)
at r.<computed> (vanta.globe.min.js:1)
I have isolated Vanta into an Background Component
//Background.js
import { useState, useRef, useEffect } from "react";
import NET from "vanta/dist/vanta.globe.min"
import * as THREE from "three";
export default function Background({ width, height, children }) {
const [vantaEffect, setVantaEffect] = useState(0);
const vantaRef = useRef(null);
useEffect(() => {
if (!vantaEffect) {
setVantaEffect(
NET({
THREE,
el: vantaRef.current,
})
);
}
return () => {
if (vantaEffect) vantaEffect.destroy();
};
}, [vantaEffect]);
return (
<div ref={vantaRef}>{children}</div>
)
}
And added the THREE script into my _app.js
import '../styles/globals.css'
import Head from "next/head";
import Navbar from "../components/Navbar";
import { useEffect } from "react";
export default function App({ Component, pageProps }) {
useEffect(() => {
const threeScript = document.createElement("script");
threeScript.setAttribute("id", "threeScript");
threeScript.setAttribute(
"src",
"https://cdnjs.cloudflare.com/ajax/libs/three.js/r121/three.min.js"
);
document.getElementsByTagName("head")[0].appendChild(threeScript);
return () => {
if (threeScript) {
threeScript.remove();
}
};
}, []);
return (
<>
<Head>
<title>BrainStorm Tutoring</title>
</Head>
<Navbar />
<Component {...pageProps} />
</>
)
}
and used it like so
//index
import Background from "../components/Background";
export default function Home() {
return (
<Background height="400" width="400">
<h1 className="text-white text-8xl text-left p-36">Fish Bowl</h1>
</Background >
)
}
Is it something wrong with THREE, or is it that next.js can't support vanta?
I have that issue with Halo, so i think the THREE object was not available or was not defined in the HALO.js file.
So i go to the official github repo of Vanta and take the source of Halo and Net (the tutorial effect) file, and i found constructor was missing in the Halo file. So i take the one of Net and put in the Halo file.
constructor(userOptions) {
THREE = userOptions.THREE || THREE;
super(userOptions);
}
Then i import my custom Halo file for the effect and it works.
I was playing around with this and found that, if I keep the Three.js version to 122. I don't get the error. Apparently any version after that has a breaking change.
I'm trying to make a music player application with Electron & React. I have been able to access music files in a resources/music folder when built and installed but I have to give the exact path to an mp3.
I have been trying ways to scan the directory and get all music file names to put in a JSON as the idea is the user could add their own music files to the music folder to be picked up by the app and displayed in a list.
Most solutions I see use node fs but I just can't get it to work. I keep getting this error:
TypeError: fs.readdir is not a function
Any help would be greatly appreciated.
import React, { Component } from 'react';
import { Link } from "react-router-dom"
import Testpage from "./Test";
import song from '../Music/Taxi.mp3'
import FileSearch from "../filesearch";
const fileSearch = new FileSearch();
const dataPath =
process.env.NODE_ENV === 'development'
? song
: 'file:///resources/Music/Taxi.mp3';
class Home extends Component {
constructor(props) {
super(props);
this.state = {};
}
componentDidMount() {
fileSearch.search()
}
render() {
return (
<div id="page">
<h1>laceHolder Home</h1>
<div>
<Testpage song={dataPath}/>
</div>
</div>
);
}
}
export default Home;
const testFolder = './';
const fs = require('fs');
export default class FileSearch {
search = () => {
fs.readdir(testFolder, (err, files) => {
files.forEach(file => {
console.log(file);
});
});
}
}
Solved, after hours of looking (im pretty new to react and electron),
I used the below instead of vanilla fs import and it worked!
const remote = window.require('electron').remote;
const electronFs = remote.require('fs');
const electronDialog = remote.dialog;
Thanks for your comments to look at window.require