Giter Site home page Giter Site logo

PROFILE

A profile site w/ Vercel & Supabase.

1.

Next.js starter.

npx create-next-app
# v9.5.0
project name >> [projectname]
TypeScript? >> yes
ESLint? >> no
Tailwind CSS? >> yes
`src/` dir >> yes
`app/` dir >> no
import alias >> @/*

2.

cd [projectname]
npm i --save-dev [package] [package] ...
  • eslint
  • stylelint
  • stylelint-scss
  • stylelint-config-standard
  • stylelint-config-standard-scss
  • stylelint-config-recess-order
  • prettier
  • eslint-config-prettier
  • eslint-config-next

Note

npm run dev

Open localhost:3000 with your browser to see the result.

3.

Make files.

  • prettier.config.js
  • stylelint.config.js
  • .eslintrc.json
  • .vscode/settings.json

Edit files.

  • tsconfig.json
  • package.json

tsconfig.json

"compilerOptions": {
  "baseUrl": ".",
  "paths": {
    "@/*": ["./src/*"]
  }
}

package.json

"scripts": {
  "export": "next build && next export",
  "eslint:fix": "eslint src --ext .js,jsx,.ts,.tsx --fix",
  "stylelint:fix": "stylelint src/**/*.{css,scss} --fix",
  "prettier:fix": "prettier --check --write 'src/**/*.{js,jsx,ts,tsx,css,scss,md,mdx}'",
  "format": "npm run eslint:fix && npm run stylelint:fix && npm run prettier:fix"
}

VS Code Extentions

Tailwind CSS

h-screen doesn't work correct on iOS.

You need to modify tailwind.config.js. (This is effective on iOS 15.4 and later)

module.exports = {
  theme: {
    extend: {
      height: {
        screen: ["100vh", "100dvh"],
      },
      minHeight: {
        screen: ["100vh", "100dvh"],
      },
      maxHeight: {
        screen: ["100vh", "100dvh"],
      },
    },
  },
};

Color Theme

This project can switch the color themes by next-theme and uses the color palette of Tailwind CSS as the theme colors.

tailwind.config.js

module.exports = {
  darkMode: "class",
};

_app.tsx

<ThemeProvider attribute="class" defaultTheme="system" enableSystem={true}>
  {children}
</ThemeProvider>

Color palette of theme

light {
  backgroud-color: --bg-zinc-50;
}
dark {
  backgroud-color: --bg-zinc-900;
  text-color: --text-white;
}
footer {
  backgroud-color: --bg-zinc-800;
  text-color: --text-white;
}
<element className="bg-zinc-50 dark:bg-zinc-900 dark:text-white" />

Next.JS

This project uses SSR to render pages. Although I wanted to use SSG and Dynamic Routes as generate a URI but Vercel's caching works too strong and won't let to revalidate.

It is not smart that render every time, so I used Cache-Control to reduce wait times of loading.

context.res.setHeader("Cache-Control", "public, s-maxage=300, stale-while-revalidate=300");

Note

Let me know if you have a better way. As the blog page have more content, I'll eventually have to consider another way for optimum performance.

You can check more details on Data Fetching.

Supabase

This project uses Supabase as DB.

Uses Database Functions and also clients call with the rpc() method.

DB has 3 tables.

  • posts
  • post
  • users

Here's an ER diagram.

ERD

posts

create table posts (
  id int4 not null primary key references post(id) on delete cascade on update cascade,
  thumbnail text not null default ''
)

post

create table post (
  id int4 not null generated always as identity primary key,
  title text not null default '',
  body text not null default '',
  created_at timestamptz not null default now(),
  updated_at timestamptz not null default now(),
  user_id int4 default null references users(id)
)

users

create table users (
  id int4 not null generated always as identity primary key,
  name text not null default '',
  avatar uuid not null default uuid_generate_v4(),
  created_at timestamptz not null default now()
)

Database Functions

Created these functions.

  • getposts
  • getpost

getposts

The getposts returns all of posts data.

Note

Also limited to 9 rows per an access.

Here's a function of getposts.

create or replace function public.getposts()
returns table (
  id int4,
  thumbnail text,
  created_at timestamptz,
  updated_at timestamptz,
  title text,
  body text,
  user_id int4,
  user_name text,
  avatar uuid
)
language sql
as $$
  select posts.id, posts.thumbnail, post.created_at, post.updated_at, post.title, post.body, post.user_id, users.name as user_name, users.avatar from posts inner join post on posts.id = post.id left outer join users on post.user_id = users.id order by created_at desc
$$;

getpost

The getpost returns a specific post data. You can get a post data by passing a parameter of slug.

Note

slug like post_id as /1, /2

Here's a function of getpost.

create or replace function public.getpost(slug int4)
returns table (
  id int4,
  created_at timestamptz,
  updated_at timestamptz,
  title text,
  body text,
  user_id int4,
  user_name text,
  avatar uuid
)
language sql
as $$
  select post.id, post.created_at, post.updated_at, post.title, post.body, post.user_id, users.name as user_name, users.avatar from post left outer join users on post.user_id = users.id where post.id = slug
$$;

You can get JSON data as using by API routes.

The posts data on localhost:3000/api/v1/posts.

A single page on localhost:3000/api/v1/post?s=1.

Note

s=[slug]

Avatar

To get avatar images from an external URL, you need to modify next.config.js.

Here's an example values.

module.exports = {
  images: {
    remotePatterns: [
      {
        protocol: "https",
        hostname: "example.com",
        port: "",
        pathname: "/account123/**",
      },
    ],
  },
};

Check more details on next/image Un-configured Host.

Icons

All of the icons which can get from npm as packages have some issues. In iconoir, all icons were loaded when import even one of the icons.

For more details, check the profile/issues#3.

To resolve this, I used @svgr/webpack. This solved the profile/issues#3 and reduced modules that avg. 1200 to avg. 200.

next.config.js

module.exports = {
  webpack: (config) => {
    config.module.rules.push({
      test: /\.svg$/i,
      issuer: /\.[jt]sx?$/i,
      use: [
        {
          loader: "@svgr/webpack",
          options: {
            icon: "1.5em", // default size of iconoir is 1.5em
            svgo: false,
            replaceAttrValues: {
              "#000000": "{props.color}",
              black: "{props.color}",
              "#FFFFFF": "{props.color}",
              white: "{props.color}",
            },
          },
        },
      ],
    });
    return config;
  },
};

Pagination

This project uses query params based pagination.

When you access the blog page, next/router will redirect with page number like /blog?p=1. If you access with page number like /blog?p=4, you can see the page No.4 of blog page.

Note

As you might know you can change the number of cards per page with Environment Variables.

NEXT_PUBLIC_SUPABASE_LIMIT=6

The above value will display 6 cards per page.

You can see the test page on localhost:3000/blog/test.

Note

p=[pageNumber]

Markdown

This project uses Markdown for stylizing page.

Using markdown can easily stylize elements. I used react-markdown and rehype-raw to render markdown & parse HTMLs.

To maximize performance, some elements are replaced to Next's components.

<ReactMarkdown
  className="markdown-wrap"
  rehypePlugins={[rehypeRaw]}
  components={{
    h1: "h2",
    h2: "h3",
    h3: "h4",
    h4: "h5",
    a: (props) => LinkBlock(props),
    img: (props) => ImageBlock(props),
    iframe: (props) => IframeBlock(props),
  }}
>
  {children}
</ReactMarkdown>
function LinkBlock(props: any): ReactElement {
  if (props.href.match("http")) {
    return (
      <a href={props.href} target="_blank" rel="noopener noreferrer">
        {props.children}
      </a>
    );
  } else {
    return <Link href={props.href}>{props.children}</Link>;
  }
}
function ImageBlock(props: any): ReactElement {
  return (
    <span className="flex items-center justify-center">
      <Image
        className="w-full md:w-4/5 h-full"
        src={props.src}
        alt={props.alt}
        width={256}
        height={256}
        sizes="100vw"
        priority
      />
    </span>
  );
}
function IframeBlock(props: any): ReactElement {
  return (
    <div className="flex items-center justify-center">
      <iframe
        width={props.width}
        height={props.height}
        src={props.src}
        title={props.title}
        allow={props.allow}
        allowFullScreen={props.allowfullscreen}
      ></iframe>
    </div>
  );
}

Check more details of supported markdown styles on localhost:3000/markdown.

Note

I'll update the markdown styles as needed.

Card style

When you use markdown for stylizing page, syntax characters of markdown will be contain in descriptions of card and meta.

To resolve this, the best way is to remove markdown syntax characters.

I used regex to remove these characters.

Here's an example.

post.body
  .replace(
    /(?<!.)[ ]*#+[ ]|(?<![0-9a-zA-Z= ])\[|\]\((http|\/).*\)|(?<!.)[ ]*-[ ]|(?<![0-9a-zA-Z ])\*{2}|\*{2}(?![0-9a-zA-Z ])|`+|<iframe.*>/gm,
    ""
  )
  .replaceAll("\n", " ");

Instruction

src/pages/

You can start editing the page by modifying pages/index.tsx. The page auto-updates as you edit the file.

The pages/ directory is mapped to /*. Files in this directory are treated as React pages.

src/pages/api/

API routes can be accessed on localhost:3000/api/hello. This endpoint can be edited in pages/api/hello.ts.

The pages/api directory is mapped to /api/*. Files in this directory are treated as API routes instead of React pages.

Fonts

This project uses next/font to automatically optimize and load Inter, a custom Google Font.

Learn More

To learn more about Next.js, take a look at the following resources:

You can check out the Next.js GitHub repository - your feedback and contributions are welcome!

Deploy on Vercel

The easiest way to deploy your Next.js app is to use the Vercel Platform from the creators of Next.js.

Check out our Next.js deployment documentation for more details.

wiyco's Projects

wiyco doesnโ€™t have any public repositories yet.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.