diff --git a/examples/with-firebase/.env.example b/examples/with-firebase/.env.example new file mode 100644 index 000000000000000..266032b8b8e96e0 --- /dev/null +++ b/examples/with-firebase/.env.example @@ -0,0 +1,8 @@ +# TODO. Fill in with Firebase Config +FIREBASE_API_KEY= +FIREBASE_AUTH_DOMAIN= +FIREBASE_DATABASE_URL= +FIREBASE_PROJECT_ID= +FIREBASE_STORAGE_BUCKET= +FIREBASE_MESSAGING_SENDER_ID= +FIREBASE_APP_ID= \ No newline at end of file diff --git a/examples/with-firebase/.gitignore b/examples/with-firebase/.gitignore new file mode 100644 index 000000000000000..cea93e82af5325c --- /dev/null +++ b/examples/with-firebase/.gitignore @@ -0,0 +1,25 @@ +# See https://help.github.com/articles/ignoring-files/ for more about ignoring files. + +# dependencies +/node_modules +/.pnp +.pnp.js + +# testing +/coverage + +# next.js +/.next/ +/out/ + +# production +/build + +# misc +.DS_Store +.env + +# debug +npm-debug.log* +yarn-debug.log* +yarn-error.log* diff --git a/examples/with-firebase/README.md b/examples/with-firebase/README.md new file mode 100644 index 000000000000000..da1dfeff9b41592 --- /dev/null +++ b/examples/with-firebase/README.md @@ -0,0 +1,57 @@ +# With Firebase + +This is a simple set up for Firebase for client side applications. + +The firebase app is initialized in `firebase/clientApp.js`, to use you just have to import it anywhere in the app + +The React Context API is used to provide user state. + +## Deploy your own + +Deploy the example using [ZEIT Now](https://zeit.co/now): + +[![Deploy with ZEIT Now](https://zeit.co/button)](https://zeit.co/import/project?template=https://github.com/zeit/next.js/tree/canary/examples/with-firebase) + +## How to use + +### Using `create-next-app` + +Execute [`create-next-app`](https://github.com/zeit/next.js/tree/canary/packages/create-next-app) with [npm](https://docs.npmjs.com/cli/init) or [Yarn](https://yarnpkg.com/lang/en/docs/cli/create/) to bootstrap the example: + +```bash +npx create-next-app --example with-firebase with-firebase-app +# or +yarn create next-app --example with-firebase with-firebase-app +``` + +### Download manually + +Download the example: + +```bash +curl https://codeload.github.com/zeit/next.js/tar.gz/canary | tar -xz --strip=2 next.js-canary/examples/with-firebase +cd with-firebase +``` + +### Configuration + +1. [Create a Firebase project](https://console.firebase.google.com/u/0/) and add a new app to it. +2. Create a `.env` file and copy the contents of `.env.example` into it: + +```bash +cp .env.example .env +``` + +3. Set each variable on `.env` with your Firebase Configuration (found in "Project settings"). + +Install it and run: + +```bash +npm install +npm run dev +# or +yarn +yarn dev +``` + +Deploy it to the cloud with [ZEIT Now](https://zeit.co/import?filter=next.js&utm_source=github&utm_medium=readme&utm_campaign=next-example) ([Documentation](https://nextjs.org/docs/deployment)). diff --git a/examples/with-firebase/context/userContext.js b/examples/with-firebase/context/userContext.js new file mode 100644 index 000000000000000..dd82753f87a072a --- /dev/null +++ b/examples/with-firebase/context/userContext.js @@ -0,0 +1,40 @@ +import { useState, useEffect, createContext, useContext } from 'react' +import firebase from '../firebase/clientApp' + +export const UserContext = createContext() + +export default ({ children }) => { + const [user, setUser] = useState(null) + const [loadingUser, setLoadingUser] = useState(true) // Helpful, to update the UI accordingly. + + useEffect(() => { + // Listen authenticated user + const unsubscriber = firebase.auth().onAuthStateChanged(async user => { + try { + if (user) { + // User is signed in. + const { uid, displayName, email, photoURL } = user + // You could also look for the user doc in your Firestore (if you have one): + // const userDoc = await firebase.firestore().doc(`users/${uid}`).get() + setUser({ uid, displayName, email, photoURL }) + } else setUser(null) + } catch (error) { + // Most probably a connection error. Handle appropiately. + } finally { + setLoadingUser(false) + } + }) + + // Unsubscribe auth listener on unmount + return () => unsubscriber() + }, []) + + return ( + + {children} + + ) +} + +// Custom hook that shorhands the context! +export const useUser = () => useContext(UserContext) diff --git a/examples/with-firebase/firebase/clientApp.js b/examples/with-firebase/firebase/clientApp.js new file mode 100644 index 000000000000000..32036c70c776076 --- /dev/null +++ b/examples/with-firebase/firebase/clientApp.js @@ -0,0 +1,24 @@ +import firebase from 'firebase/app' +import 'firebase/auth' // If you need it +import 'firebase/firestore' // If you need it +import 'firebase/storage' // If you need it +import 'firebase/analytics' // If you need it + +const clientCredentials = { + apiKey: process.env.FIREBASE_API_KEY, + authDomain: process.env.FIREBASE_AUTH_DOMAIN, + databaseURL: process.env.FIREBASE_DATABASE_URL, + projectId: process.env.FIREBASE_PROJECT_ID, + storageBucket: process.env.FIREBASE_STORAGE_BUCKET, + messagingSenderId: process.env.FIREBASE_MESSAGING_SENDER_ID, + appId: process.env.FIREBASE_APP_ID, +} + +// Check that `window` is in scope for the analytics module! +if (typeof window !== 'undefined' && !firebase.apps.length) { + firebase.initializeApp(clientCredentials) + // To enable analytics. https://firebase.google.com/docs/analytics/get-started + if ('measurementId' in clientCredentials) firebase.analytics() +} + +export default firebase diff --git a/examples/with-firebase/next.config.js b/examples/with-firebase/next.config.js new file mode 100644 index 000000000000000..7bd8ac5a276545e --- /dev/null +++ b/examples/with-firebase/next.config.js @@ -0,0 +1,14 @@ +// On production, variables are set with `now secrets`. On development, they use the .env file +require('dotenv').config() + +module.exports = { + env: { + FIREBASE_API_KEY: process.env.FIREBASE_API_KEY, + FIREBASE_AUTH_DOMAIN: process.env.FIREBASE_AUTH_DOMAIN, + FIREBASE_DATABASE_URL: process.env.FIREBASE_DATABASE_URL, + FIREBASE_PROJECT_ID: process.env.FIREBASE_PROJECT_ID, + FIREBASE_STORAGE_BUCKET: process.env.FIREBASE_STORAGE_BUCKET, + FIREBASE_MESSAGING_SENDER_ID: process.env.FIREBASE_MESSAGING_SENDER_ID, + FIREBASE_APP_ID: process.env.FIREBASE_APP_ID, + }, +} diff --git a/examples/with-firebase/now.json b/examples/with-firebase/now.json new file mode 100644 index 000000000000000..8adcc56767429f3 --- /dev/null +++ b/examples/with-firebase/now.json @@ -0,0 +1,13 @@ +{ + "build": { + "env": { + "FIREBASE_API_KEY": "@firebase-api-key", + "FIREBASE_AUTH_DOMAIN": "@firebase-auth-domain", + "FIREBASE_DATABASE_URL": "@firebase-database-url", + "FIREBASE_PROJECT_ID": "@firebase-project-id", + "FIREBASE_STORAGE_BUCKET": "@firebase-storage-bucket", + "FIREBASE_MESSAGING_SENDER_ID": "@firebase-messaging-sender-id", + "FIREBASE_APP_ID": "@firebase-app-id" + } + } +} diff --git a/examples/with-firebase/package.json b/examples/with-firebase/package.json new file mode 100644 index 000000000000000..828fa643cbe29cc --- /dev/null +++ b/examples/with-firebase/package.json @@ -0,0 +1,18 @@ +{ + "name": "with-firebase", + "version": "0.1.0", + "scripts": { + "dev": "next dev", + "build": "next build", + "start": "next start" + }, + "dependencies": { + "firebase": "7.11.0", + "next": "latest", + "react": "^16.13.0", + "react-dom": "^16.13.0" + }, + "devDependencies": { + "dotenv": "8.2.0" + } +} diff --git a/examples/with-firebase/pages/_app.js b/examples/with-firebase/pages/_app.js new file mode 100644 index 000000000000000..c6985b38a61d555 --- /dev/null +++ b/examples/with-firebase/pages/_app.js @@ -0,0 +1,8 @@ +import UserProvider from '../context/userContext' + +// Custom App to wrap it with context provider +export default ({ Component, pageProps }) => ( + + + +) diff --git a/examples/with-firebase/pages/index.js b/examples/with-firebase/pages/index.js new file mode 100644 index 000000000000000..71d05d886cda673 --- /dev/null +++ b/examples/with-firebase/pages/index.js @@ -0,0 +1,183 @@ +import Head from 'next/head' +import { useEffect } from 'react' +import { useUser } from '../context/userContext' +import firebase from '../firebase/clientApp' + +export default () => { + // Our custom hook to get context values + const { loadingUser, user } = useUser() + + useEffect(() => { + if (!loadingUser) { + // You know that the user is loaded: either logged in or out! + console.log(user) + } + // You also have your firebase app initialized + console.log(firebase) + }, [loadingUser, user]) + + return ( +
+ + Next.js w/ Firebase Client-Side + + + +
+

Next.js w/ Firebase Client-Side

+

Fill in your credentials to get started

+
+ + + + + + +
+ ) +} diff --git a/examples/with-firebase/public/favicon.ico b/examples/with-firebase/public/favicon.ico new file mode 100644 index 000000000000000..4965832f2c9b060 Binary files /dev/null and b/examples/with-firebase/public/favicon.ico differ diff --git a/examples/with-firebase/public/zeit.svg b/examples/with-firebase/public/zeit.svg new file mode 100644 index 000000000000000..dd3916c5f04996d --- /dev/null +++ b/examples/with-firebase/public/zeit.svg @@ -0,0 +1,10 @@ + + + + + + + + + +