Portfolio Minimal is a Gatsby Theme. You can install it as a dependency in your project and use its building blocks to create your own site - effortless and within minutes.
Example Project on GitHub
This repository also contains an example-site that shows how the theme integrates into a Gatsby site.
- Gatsby Theme Portfolio Minimal - A modern one-page portfolio with a clean yet expressive design.
In general, Gatsby Themes are regular Node packages that can be installed like any other package via npm or yarn.
To use Gatsby Theme Portfolio Minimal, you need to have a Gatsby project. If you have an existing one, you can skip the next part and follow the instructions for adding Portfolio Minimal to an existing Gatsby site.
If you are creating a new site and want to use the Gatsby Theme Portfolio Minimal, you can use the Gatsby Starter Portfolio Minimal Theme. This will generate a new site that already has the theme pre-configured.
-
Install the Gatsby CLI
npm install -g gatsby-cli
-
Create a new Gatsby site with the Portfolio Minimal Starter.
gatsby new portfolio-minimal https://github.com/konstantinmuenster/gatsby-starter-portfolio-minimal-theme
-
Once installed, you can begin developing your site.
cd portfolio-minimal gatsby develop
-
By default, the Portfolio Minimal Starter has a
content
directory at the root of your Gatsby site. There, you can edit the theme settings as well as add content for your sections. To learn more about it, see this section: Using The Content Directory
If you already have a site you’d like to add the theme to, you can install it and manually configure it.
-
Install the Gatsby Theme Portfolio Minimal via npm or yarn.
npm install gatsby-theme-portfolio-minimal
-
Add the theme to the plugin list in your
gatsby-config.js
file.module.exports = { plugins: [`gatsby-theme-portfolio-minimal`], };
-
Run development mode to initialize the theme's configuration files.
gatsby develop
-
By default, this creates a
content
directory at the root of your Gatsby site. There, you can edit the theme settings as well as add content for your sections. To learn more about it, see this section: Using The Content Directory
The concept behind themes is easy. You have the theme installed as a npm package which allows you to extract certain functionalities from it when building your site. In the case of Portfolio Minimal, you can extract and configure various components that can be used as building blocks to construct a page. Let's see how it works!
-
Creating pages with Portfolio Minimal can be done within minutes. To make it work, you have to create a new file within the
pages
directory of your site. This is where all pages are located in a Gatsby site.portfolio-minimal-site ├── src │ └── pages │ └── first-page.js ├── gatsby-config.js └── package.json
Note that the file name will be used as a URL route. So in our example, the page can be requested on the
/first-page
route. -
Inside
first-page.js
, we create a React component which will be exported by default. TheFirstPage
component uses thePage
andSeo
components which are being imported from thegatsby-theme-portfolio-minimal
package. While thePage
component is mandatory to construct a page with Portfolio Minimal,Seo
isn't (but recommended).import { Page, Seo } from 'gatsby-theme-portfolio-minimal'; export default function FirstPage() { return ( <> <Seo title="FirstPage" /> <Page useSplashScreenAnimation>...</Page> </> ); }
Note that we pass
useSplashScreenAnimation
as a prop to thePage
component. To find out more about how you can configure the theme's components, check out this section: Theme Components -
Now that we have our basic page layout set up, we can start adding sections to the page. It basically works the same as before: You import components from the
gatsby-theme-portfolio-minimal
package which then can be configured and used in your page.import { AboutSection, Page, Seo } from 'gatsby-theme-portfolio-minimal'; export default function FirstPage() { return ( <> <Seo title="FirstPage" /> <Page useSplashScreenAnimation> <AboutSection sectionId="about" heading="About Portfolio Minimal" /> </Page> </> ); }
-
That's it! You can now visit your page on
/first-page
. To see which components are available to create pages, have a look at Theme Components.
The theme was designed with the following paradigms in mind:
-
Portfolio Minimal aims to separate content from code. That's why almost all content should live in its own directory. This is, by default, a folder at the root of your site called
content
. A different root content directory can be specified viagatsby-config.js
. -
The theme completely capsules all data sourcing for you. Hence, there is no need to configure any data sources. You just have to place your content files in the right directory and it just works ;)
This is how the content directory is structured:
content # can be specified in gatsby-config.js
├── articles # mandatory folder, cannot be renamed
├── images # mandatory folder, cannot be renamed
├── sections # mandatory folder, cannot be renamed
└── settings.json # mandatory file, cannot be renamed
This is where all your blog posts live (if you use the blog integration feature). Each blog post should have its own folder. The folder name is then used to derive a proper slug for the URL. More about how to use the blog integration feature can be found here: Using The Blog Integration Feature.
This is where all your images are being stored. Each image can then be referenced in a content file by specifying the relative path to it. For instance, if you want to use an image inside your contact.json
file, you can do so by referencing the image file like this: "src": "../../images/favicon.png"
This is where all the content for your sections is primarily stored. Inside the sections
folder, each section type has its own subfolder and an associated file type (either Markdown or JSON) that is based on the kind of data structure best suited for the section content.
content
└── sections
│ └── about
│ └── about.md # has to be Markdown
│ └── contact
│ └── contact.json # has to be JSON
│ └── hero
│ └── hero.json # has to be JSON
│ └── interests
│ └── interests.json # has to be JSON
│ └── legal
│ └── imprint.md # has to be Markdown
│ └── projects
│ └── projects.json # has to be JSON
This file stores your global site settings, e.g. toggles for certain features. If you want to learn which settings you can configure, switch to the Customization part of the documentation.
If you want to extend your portfolio site with a blog, you can do so easily with Portfolio Minimal.
-
Enable the blog integration through adding the blog configuration in your
gatsby-config.js
:module.exports = { plugins: [ { resolve: 'gatsby-theme-portfolio-minimal', options: { blogSettings: { entityName: 'Article', path: '/blog', usePathPrefixForArticles: false, }, }, }, ], };
-
Inside the
articles
folder of your content directory, create a new folder to add your first article. The folder name will be used for generating the URL slug of the article.content └── articles │ └── my-first-article
-
Inside the
my-first-article
folder, create a Markdown file with the following shape.--- title: 'This is an exemplary article for the blog.' description: 'This description will be used for the article listing and search results on Google.' date: '2021-05-28' banner: src: '../../images/kelly-sikkema-Hl3LUdyKRic-unsplash.jpg' alt: 'First Markdown Post' caption: 'Photo by <u><a href="https://example.com">Example</a></u>' categories: - 'Setup' keywords: - 'Example' - 'Gatsby' --- This will be the actual content of the article.
-
Start the development server and you will see that two pages are being creating during the build process.
-
https://yoursite.com/blog
: This is the article listing page containing all your posts. -
https://yoursite.com/my-first-article
: This is the page for the article you added through your Markdown file.
-
The theme exposes sections and other components as building blocks to construct your page easily.
Most section components work the following way: You import and configure the component inside your page file and add the associated content in the correct content directory path.
The following table shows which section components are available and how they can be configured.
Section Component | Available Props | Associated Content File |
---|---|---|
AboutSection |
sectionId (required) heading (optional)
|
*contentRootDir*/sections/about/*yourFile*.md |
ContactSection |
sectionId (required) heading (optional)
|
*contentRootDir*/sections/contact/*yourFile*.json |
HeroSection |
sectionId (required)
|
*contentRootDir*/sections/hero/*yourFile*.json |
InterestsSection |
sectionId (required) heading (optional)
|
*contentRootDir*/sections/interests/*yourFile*.json |
LegalSection |
sectionId (required) heading (optional)
|
*contentRootDir*/sections/legal/*yourFile*.md |
ProjectsSection |
sectionId (required) heading (optional)
|
*contentRootDir*/sections/projects/*yourFile*.json |
There are other components which help you in constructing and enriching pages.
The following table shows which utility components are available and how they can be configured.
Utility Component | Available Props | Description |
---|---|---|
Page |
useSplashScreenAnimation (optional)
|
Mandatory component to construct a page with a proper layout. |
Seo |
title (required) useTitleTemplate (optional) description (optional) noIndex (optional)
|
Optional, yet recommended, component to enrich a page with SEO data. |
I tried to leave as much configuration as possible to you as a developer. This allows the theme to be highly flexible and adjustable to your needs.
The theme settings can be divided in two parts. There is a settings.json
file inside the content directory. Also, you can set settings for the theme inside your gatsby-config.js
.
settings.json
can be understood as your global site settings. The configuration option inside are primarily used to specify how your layout should look like and which functionality you want to use.
{
"siteMetadata": {
"language": "en", // Site language for SEO
"siteUrl": "", // Site URL for SEO
"thumbnail": "", // Path to image. Displayed if site gets shared (e.g. on LinkedIn).
"title": "", // Default SEO title
"titleTemplate": "%s · Portfolio", // SEO Title title (%s will be replaced by title of page)
"description": "", // Default SEO description
"author": "", // Author's name for blog article footer
"avatar": "", // Author's avatar for blog article footer
"bio": "", // Author's biography for blog article footer
"social": { // Social Profiles for various features
"behance": "",
"buymeacoffee": "",
"devto": "",
"discord": "",
"dribble": "",
"github": "",
"gitlab": "",
"goodreads": "",
"hashnode": "",
"instagram": "",
"linkedin": "",
"mail": "",
"mastodon": "",
"medium": "",
"twitter": "",
"twitch": "",
"patreon": "",
"reddit": "",
"untappd": "",
"stackoverflow": "",
"youtube": "",
}
},
"siteConfiguration": {
"logo": {
"text": "",
"image": "", // Relative path to image
"imageDark": "", // Used on dark backgrounds
},
"navigation": {
"header": [
{ "label": "About", "url": "/#about" },
{ "label": "Blog", "url": "/blog" },
{ "label": "Features", "url": "/#features" },
{ "label": "Github", "url": "/#github" }
],
"ctaButton": { "openNewTab": true, "label": "Resume", "url": "/resume.pdf" },
"footer": [
{ "label": "Privacy", "url": "/privacy" },
{ "label": "Imprint", "url": "/imprint" }
]
},
"featureToggles": {
"useDarkModeAsDefault": false,
"useDarkModeBasedOnUsersPreference": true,
"useCookieBar": false
}
}
}
When adding the gatsby-theme-portfolio-minimal
package to the array of plugins, you can specify numerous options to adjust the functionality to your needs.
module.exports = {
plugins: [
{
resolve: 'gatsby-theme-portfolio-minimal',
options: {
siteUrl: '', // Used for sitemap generation
manifestSettings: {
favicon: '', // Path is relative to the root
siteName: '', // Used in manifest.json
shortName: '', // Used in manifest.json
startUrl: '', // Used in manifest.json
backgroundColor: '', // Used in manifest.json
themeColor: '', // Used in manifest.json
display: '', // Used in manifest.json
},
contentDirectory: '', // Specifies the root content directory path
skipContentDirectorySetup: true, // Default false
disableGatsbyPluginOffline: true, // Default false
blogSettings: {
// If set, the blog integration is enabled
entityName: '', // Defines naming of posts throughout site (e.g. title of blog post listing)
path: '', // Defines the slug for the blog listing page
usePathPrefixForArticles: false, // Default true (i.e. path will be /blog/first-article)
},
plausibleAnalytics: {
// If set, the Plausible.io analytics integration is enabled
domain: 'example.com',
},
googleAnalytics: {
// If set, the Google Analytics integration is enabled
trackingId: '', // e.g. UA-XXXXXX-X
anonymize: true, // Default is true
environments: [], // Default ["production"]
},
},
},
],
};
Besides the prebuilt sections, such as <HeroSection />
, the theme also exposes building blocks for your own, customized sections. From the theme's package, you can import the Animation
and Section
component, to build your own sections like.
import { HeroSection, Page, Section, Animation } from 'gatsby-theme-portfolio-minimal';
export default function IndexPage() {
return (
<Page>
<HeroSection sectionId="hero" />
<Animation type="fadeUp">
<Section heading="This is a brand new section.">
<p>You can add your custom JSX here.</p>
</Section>
</Animation>
</Page>
);
}
Internally, Portfolio Minimal uses CSS variables to ensure a consistent color scheme across the site. You can override these colors by using the concept of shadowing Gatsby offers. To do this, follow the instructions:
-
Create a file called
theme.css
in the following path:{GatsbyProjectRoot}/src/gatsby-theme-portfolio-minimal/globalStyles
. -
Add the following contents to the file and adjust the colors as you like. Do not change the names of the variables!
body[data-theme='lightTheme'] { --primary-color: #000000; --secondary-color: #fff4d9; --tertiary-color: #f2f2f2; --text-color: #000000; --subtext-color: #555555; --background-color: #ffffff; --card-background-color: #ffffff; --scroll-bar-color: rgba(0, 0, 0, 0.5); --box-shadow-color: rgba(0, 0, 0, 0.16); --box-shadow-hover-color: rgba(0, 0, 0, 0.32); --base-font: 'Roboto', Arial, sans-serif; --code-block-bg: #151f28; --code-block-var: #ff39a7; --code-block-num: #fed703; --code-block-fun: #c653ff; --code-block-keyword: #2bdcff; --code-block-comment: #8292a2; --code-block-tkimp: #fd971f; } body[data-theme='darkTheme'] { --primary-color: #fafafa; --secondary-color: #2a2926; --tertiary-color: #252525; --text-color: rgba(255, 255, 255, 0.87); --subtext-color: #aaaaaa; --background-color: #121212; --card-background-color: #1c1c1c; --scroll-bar-color: rgba(255, 255, 255, 0.5); --box-shadow-color: rgba(0, 0, 0, 0.16); --box-shadow-hover-color: rgba(0, 0, 0, 0.32); --base-font: 'Roboto', Arial, sans-serif; --code-block-bg: #151f28; --code-block-var: #ff39a7; --code-block-num: #fed703; --code-block-fun: #c653ff; --code-block-keyword: #2bdcff; --code-block-comment: #8292a2; --code-block-tkimp: #fd971f; }
If you find any bugs or have feature suggestions, create a new issue or pull request 🙏
Thanks a lot for using this starter! 💪
Konstantin Münster – konstantin.digital