soulmachine/nextjs-starter-kit
Điểm qua soulmachine/nextjs-starter-kit: 105 sao trên GitHub, viết chủ yếu bằng JavaScript. A Next.js + Antd + MobX + Typescript starter kit
Tóm tắt dựng từ metadata GitHub của chính dự án — chưa có bài review TopGit. Trang sẽ tự động cập nhật khi bài review đầy đủ được xuất bản.
TopGit viết bài đầy đủ cho repo có nhiều sao nhất và được yêu cầu nhiều nhất. Trang này là snapshot trong thời gian chờ — xem README gốc ở tab READ ME.
Snapshot
Cộng tác viên hàng đầu
Xem cộng tác viên hàng đầu
nextjs-starter-kit
Next.js + Antd + MobX + react-intl + Typescript starter kit.
Table of Contents
- Step1: Create an empty Next.js project
- Step2: Ant Design
- Step3: MobX
- Step4: React Intl
- Step5: TypeScript
Step1: Create an empty Next.js project
Create an empty npm project,
npm init
Fill in some information then we get a package.json file:
{
"name": "nextjs-starter-kit",
"version": "1.0.0",
"description": "Next.js starter kit",
"author": "[email protected]",
"license": "Apache-2.0",
"repository": {
"type": "git",
"url": "git+https://github.com/soulmachine/nextjs-starter-kit.git"
},
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
}
}
Install Next.js,
npm install next react react-dom --save
and add a script to your package.json like this:
{
"scripts": {
"dev": "next",
"build": "next build",
"start": "next start"
}
}
Finally we get a complete package.json file:
{
"name": "nextjs-starter-kit",
"version": "1.0.0",
"description": "Next.js starter kit",
"author": "[email protected]",
"license": "Apache-2.0",
"repository": {
"type": "git",
"url": "git+https://github.com/soulmachine/nextjs-starter-kit.git"
},
"scripts": {
"dev": "next",
"build": "next build",
"start": "next start"
},
"dependencies": {
"next": "^5.0.0",
"react": "^16.2.0",
"react-dom": "^16.2.0"
}
}
Create a file ./pages/index.js inside your project:
export default () => <div>Welcome to next.js!</div>
and then just run npm run dev and go to http://localhost:3000.
Step2: Ant Design
There are two hight quality React UI libaries, ant-design and material-ui, I choose ant-design because:
- Ant Design supports react-native officially, ant-mobile, while material-ui doesn't yet
- material-ui hasn't reached to 1.0 yet
2.1 Install antd
npm install antd --save
2.2 Use modularized antd
Install the Babel plugin babel-plugin-import,
npm install babel-plugin-import --save-dev
Enable it in .babelrc, the .babelrc file is copied from the official example, next.js/examples/with-ant-design/.babelrc
{
"presets": ["next/babel"],
"plugins": [
["import", { "libraryName": "antd", "style": false }]
]
}
2.3 Import Antd CSS
Note that although we've enabled the plugin, but we pass false to the plugin's config to tell it to skip all CSS from antd, instead we'll load antd's css from CDN. This ugly solution is due to the issue that Next.js can only import CSS using styled-jsx or CSS-in-JS built in with jsx syntax and Next.js is not able to use css-loader. See the warning from official page https://github.com/zeit/next.js/#customizing-webpack-config:
Warning: Adding loaders to support new file types (css, less, svg, etc.) is not recommended because only the client code gets bundled via webpack and thus it won't work on the initial server rendering. Babel plugins are a good alternative because they're applied consistently between server/client rendering
Also see this issue Work with antd? · Issue #484 and this Importing CSS files? · Issue #544.
Create a file ./components/Layout.js in the project root directory with the following code:
import Head from 'next/head'
import { LocaleProvider } from 'antd'
import enUS from 'antd/lib/locale-provider/en_US'
export default ({ title, children }) =>
<div>
<Head>
<title>{ title }</title>
<meta name='viewport' content='width=device-width, initial-scale=1' />
<meta charSet='utf-8' />
<link rel='stylesheet' href='//cdnjs.cloudflare.com/ajax/libs/antd/3.2.0/antd.min.css' />
</Head>
<style jsx global>{`
body {
}
`}</style>
<LocaleProvider locale={enUS}>
<div>{children}</div>
</LocaleProvider>
</div>
Make sure the version number is the same as antd package.json.
Copy ./pages/index.js from the official example to ./pages/antd.js(remove LocaleProvider from it), and add a link to ./pages/index.js:
import React from 'react'
import Link from 'next/link'
export default () => <div>
<p>Welcome to next.js!</p>
<Link href='/antd'><a>Ant Design</a></Link>
</div>
run npm run dev and go to http://localhost:3000.
Step3: MobX
See the official example examples/with-mobx-state-tree.
3.1 Install mobx, mobx-react and mobx-state-tree
npm install mobx mobx-react mobx-state-tree --save
3.2 Decorator support
Decorator support is activated by the Babel plugin babel-plugin-transform-decorators-legacy,
npm install babel-plugin-transform-decorators-legacy --save-dev
Add this plugin to .babelrc:
{
"presets": ["next/babel"],
"plugins": [
"transform-decorators-legacy",
["import", { "libraryName": "antd", "style": false }]
]
}
3.3 server.js
From mobx-react's official document here, Server Side Rendering with useStaticRendering:
When using server side rendering, normal lifecycle hooks of React components are not fired, as the components are rendered only once. Since components are never unmounted, observer components would in this case leak memory when being rendered server side. To avoid leaking memory, call useStaticRendering(true) when using server side rendering. This makes sure the component won't try to react to any future data changes.
We need to customize server.js for Next.js:
const port = parseInt(process.env.PORT, 10) || 3000
const dev = process.env.NODE_ENV !== 'production'
const { createServer } = require('http')
const { parse } = require('url')
const next = require('next')
const mobxReact = require('mobx-react')
const app = next({ dev })
const handle = app.getRequestHandler()
mobxReact.useStaticRendering(true)
app.prepare().then(() => {
createServer((req, res) => {
const parsedUrl = parse(req.url, true)
handle(req, res, parsedUrl)
}).listen(port, err => {
if (err) throw err
console.log(`> Ready on http://localhost:${port}`)
})
})
And we need to update commands in scripts:
"scripts": {
"dev": "node server.js",
"build": "next build",
"start": "NODE_ENV=production node server.js"
},
3.4 ClockStore
Create a file ./stores/ClockStore.js:
import { types, applySnapshot } from "mobx-state-tree"
const ClockStore = types
.model({
lastUpdate: types.Date,
light: types.boolean,
})
.actions((self) => {
let timer;
function start() {
timer = setInterval(() => {
// mobx-state-tree doesn't allow anonymous callbacks changing data
// pass off to another action instead
self.update();
})
}
function update() {
self.lastUpdate = Date.now()
self.light = true
}
function stop() {
clearInterval(timer);
}
return { start, stop, update }
})
let clockStore = null
export default function initClockStore(isServer, snapshot = null) {
if (isServer) {
clockStore = ClockStore.create({ lastUpdate: Date.now(), light: false })
}
if (clockStore == null) {
clockStore = ClockStore.create({ lastUpdate: Date.now(), light: false })
}
if (snapshot) {
applySnapshot(clockStore, snapshot)
}
return clockStore
}
The trick here for supporting universal mobx is to separate the cases for the client and the server. When we are on the server we want to create a new store every time, otherwise different users data will be mixed up. If we are in the client we want to use always the same store. That's what we accomplish on ClockStore.js.
3.5 ClockPage
Now let's add a new page for Clock.
First create a stateless Component in ./components/Clock.js, just copy examples/with-mobx-state-tree/components/Clock.js.
Second, create a container for Clock in ./containers/Clock.js:
import React from 'react'
import { inject, observer } from 'mobx-react'
import Clock from '../components/Clock'
@inject('clock') @observer
class ClockContainer extends React.Component {
componentDidMount() {
this.props.clock.start()
}
componentWillUnmount() {
this.props.clock.stop()
}
render() {
return (
<Clock lastUpdate={this.props.clock.lastUpdate} light={this.props.clock.light} />
)
}
}
export default ClockContainer
Third, create a new page ./pages/clock.js:
import React from 'react'
import Link from 'next/link'
import { Provider } from 'mobx-react'
import { getSnapshot } from 'mobx-state-tree'
import initClockStore from '../stores/ClockStore'
import Clock from '../containers/Clock'
class ClockPage extends React.Component {
static getInitialProps({ req }) {
const isServer = !!req
const clockStore = initClockStore(isServer)
return { initialState: getSnapshot(clockStore), isServer }
}
constructor(props) {
super(props)
this.clockStore = initClockStore(props.isServer, props.initialState)
}
render() {
return (
<Provider clock = {this.clockStore}>
<div>
<h1>Clock</h1>
<Clock />
</div>
</Provider>
)
}
}
export default ClockPage
In every page that wants to use MobX, we need to wrap it in <Provider>, and initialize its store by calling initClockStore() in getInitialProps() and constructor.
Run npm run dev and go to http://localhost:3000/clock.
Step4: React Intl
Internationalization is a must have for most Apps, we're going to use react-intl to internationalize this App.
4.1 Install packages
npm install react-intl intl babel-plugin-react-intl accepts glob --save
4.2 server.js
On the server side we need to support server-side language negotiation, see the following code:
// Polyfill Node with `Intl` that has data for all locales.
// See: https://formatjs.io/guides/runtime-environments/#server
const IntlPolyfill = require('intl')
Intl.NumberFormat = IntlPolyfill.NumberFormat
Intl.DateTimeFormat = IntlPolyfill.DateTimeFormat
const {readFileSync} = require('fs')
const {basename} = require('path')
const {createServer} = require('http')
const { parse } = require('url')
const accepts = require('accepts')
const glob = require('glob')
const next = require('next')
const mobxReact = require('mobx-react')
const port = parseInt(process.env.PORT, 10) || 3000
const dev = process.env.NODE_ENV !== 'production'
const app = next({dev})
const handle = app.getRequestHandler()
mobxReact.useStaticRendering(true)
// Get the supported languages by looking for translations in the `lang/` dir.
const languages = glob.sync('./lang/*.json').map((f) => basename(f, '.json'))
// We need to expose React Intl's locale data on the request for the user's
// locale. This function will also cache the scripts by lang in memory.
const localeDataCache = new Map()
const getLocaleDataScript = (locale) => {
const lang = locale.split('-')[0]
if (!localeDataCache.has(lang)) {
const localeDataFile = require.resolve(`react-intl/locale-data/${lang}`)
const localeDataScript = readFileSync(localeDataFile, 'utf8')
localeDataCache.set(lang, localeDataScript)
}
return localeDataCache.get(lang)
}
// We need to load and expose the translations on the request for the user's
// locale. These will only be used in production, in dev the `defaultMessage` in
// each message description in the source code will be used.
const getMessages = (locale) => {
return require(`./lang/${locale}.json`)
}
app.prepare().then(() => {
createServer((req, res) => {
const parsedUrl = parse(req.url, true)
const accept = accepts(req)
let locale = accept.language(languages)
locale = locale || 'en'
req.locale = locale
req.localeDataScript = getLocaleDataScript(locale)
req.messages = getMessages(locale)
handle(req, res, parsedUrl)
}).listen(port, (err) => {
if (err) throw err
console.log(`> Ready on http://localhost:${port}`)
})
})
4.3 _document.js
We need to inject the script localeDataScript() from react-intl to every page, so ./pages/_document.js comes to help:
import Document, {Head, Main, NextScript} from 'next/document'
// The document (which is SSR-only) needs to be customized to expose the locale
// data for the user's locale for React Intl to work in the browser.
export default class IntlDocument extends Document {
static async getInitialProps (context) {
const props = await super.getInitialProps(context)
const {req: {locale, localeDataScript}} = context
return {
...props,
locale,
localeDataScript
}
}
render () {
// Polyfill Intl API for older browsers
const polyfill = `https://cdn.polyfill.io/v2/polyfill.min.js?features=Intl.~locale.${this.props.locale}`
return (
<html>
<Head />
<body>
<Main />
<script src={polyfill} />
<script
dangerouslySetInnerHTML={{
__html: this.props.localeDataScript
}}
/>
<NextScript />
</body>
</html>
)
}
}
4.4 PageWithIntl HOC
import React, {Component} from 'react'
import {IntlProvider, addLocaleData, injectIntl} from 'react-intl'
// Register React Intl's locale data for the user's locale in the browser. This
// locale data was added to the page by `pages/_document.js`. This only happens
// once, on initial page load in the browser.
if (typeof window !== 'undefined' && window.ReactIntlLocaleData) {
Object.keys(window.ReactIntlLocaleData).forEach((lang) => {
addLocaleData(window.ReactIntlLocaleData[lang])
})
}
export default (Page) => {
const IntlPage = injectIntl(Page)
return class PageWithIntl extends Component {
static async getInitialProps (context) {
let props
if (typeof Page.getInitialProps === 'function') {
props = await Page.getInitialProps(context)
}
// Get the `locale` and `messages` from the request object on the server.
// In the browser, use the same values that the server serialized.
const {req} = context
const {locale, messages} = req || window.__NEXT_DATA__.props
// Always update the current time on page load/transition because the
// <IntlProvider> will be a new instance even with pushState routing.
const now = Date.now()
return {...props, locale, messages, now}
}
render () {
const {locale, messages, now, ...props} = this.props
return (
<IntlProvider locale={locale} messages={messages} initialNow={now}>
<IntlPage {...props} />
</IntlProvider>
)
}
}
}
4.5 Create a testing page
Let's create a testing page ./pages/react-intl.js:
import React, {Component} from 'react'
import Head from 'next/head'
import {FormattedMessage, FormattedNumber, defineMessages, FormattedRelative} from 'react-intl'
import pageWithIntl from '../components/PageWithIntl'
import Layout from '../components/Layout'
const {description} = defineMessages({
description: {
id: 'description',
defaultMessage: 'An example app integrating React Intl with Next.js'
}
})
class ReactIntlPage extends Component {
static async getInitialProps ({req}) {
return {someDate: Date.now()}
}
render () {
return (
<Layout title="React Intl">
<Head>
<meta name='description' content={this.props.intl.formatMessage(description)} />
</Head>
<p>
<FormattedMessage id='greeting' defaultMessage='Hello, World!' />
</p>
<p>
<FormattedNumber value={1000} />
</p>
<p>
<FormattedRelative
value={this.props.someDate}
updateInterval={1000}
/>
</p>
</Layout>
)
}
}
export default pageWithIntl(ReactIntlPage)
4.6 Messages files
Create a directory ./lang, put messages files here.
./lang/en.json:
{
"description": "An example app integrating React Intl with Next.js",
"greeting": "Hello, World!"
}
./lang/fr.json:
{
"description": "Un exemple d'application intégrant React Intl avec Next.js",
"greeting": "Bonjour le monde!"
}
./lang/zh.json:
{
"description": "一个将React Intl 与 Next.js 集成的例子",
"greeting": "你好,世界!"
}
Run npm run dev and go to http://localhost:3000/clock.
4.7 babel-plugin-react-intl
babel-plugin-react-intl is a plugin that extracts string messages for translation from modules that use React Intl. To make it work we need to configure it.
First enable it in .babelrc:
{
"presets": ["next/babel"],
"plugins": [
"transform-decorators-legacy",
["import", { "libraryName": "antd", "style": false }],
["react-intl", {
"messagesDir": "./lang/.messages/"
}]
]
}
"messagesDir": "lang/.messages/" means this will output a .json file corresponding to each component from which React Intl messages were extracted.
Add lang/.messages/ to the file .gitignore.
Then we write a script to merge all files under "lang/.messages/" to a single json file ./lang/en.json:
const {readFileSync, writeFileSync} = require('fs')
const {resolve} = require('path')
const glob = require('glob')
const defaultMessages = glob.sync('./lang/.messages/**/*.json')
.map((filename) => readFileSync(filename, 'utf8'))
.map((file) => JSON.parse(file))
.reduce((messages, descriptors) => {
descriptors.forEach(({id, defaultMessage}) => {
if (messages.hasOwnProperty(id)) {
throw new Error(`Duplicate message id: ${id}`)
}
messages[id] = defaultMessage
})
return messages
}, {})
writeFileSync('./lang/en.json', JSON.stringify(defaultMessages, null, 2))
console.log(`> Wrote default messages to: "${resolve('./lang/en.json')}"`)
And add this script to the build command in package.json:
"build": "next build && node ./scripts/default-lang.js",
Run npm run build and you will see it generates a file ./lang/en.json.
4.8 Antd LocaleProvider
This step is optional unless we want internationalize Antd's builtin strings.
In server.js we load :
const antdLocaleCache = new Map()
const rootToLang = {
'ar': 'ar_EG',
'bg': 'bg_BG',
'ca': 'ca_ES',
'cs': 'cs_CZ',
'de': 'de_DE',
'el': 'el_GR',
'en': 'en_US',
'es': 'es_ES',
'et': 'et_EE',
'fa': 'fa_IR',
'fi': 'fi_FI',
'fr': 'fr_FR',
'is': 'is_IS',
'it': 'it_IT',
'ja': 'ja_JP',
'ko': 'ko_KR',
'nb': 'nb_NO',
'nl': 'nl_NL',
'pl': 'pl_PL',
'pt': 'pt_PT',
'ru': 'ru_RU',
'sk': 'sk_SK',
'sr': 'sr_RS',
'sv': 'sv_SE',
'th': 'th_TH',
'tr': 'tr_TR',
'uk': 'uk_UA',
'vi': 'vi_VN',
'zh': 'zh_CN'
}
const getAntdLocaleData = (locale) => {
const root = locale.split('-')[0]
const lang = rootToLang[root]
if (!antdLocaleCache.has(lang)) {
const localeData = require(`antd/lib/locale-provider/${lang}`)
antdLocaleCache.set(lang, localeData)
}
return antdLocaleCache.get(lang)
}
And we need to wrap PageWithIntl.js with Antd LocaleProvider:
diff --git a/step4/components/PageWithIntl.js b/step4/components/PageWithIntl.js
index 1bcf0af..cd45005 100644
--- a/step4/components/PageWithIntl.js
+++ b/step4/components/PageWithIntl.js
@@ -1,5 +1,6 @@
import React, {Component} from 'react'
import {IntlProvider, addLocaleData, injectIntl} from 'react-intl'
+import LocaleProvider from 'antd/lib/locale-provider';
// Register React Intl's locale data for the user's locale in the browser. This
// locale data was added to the page by `pages/_document.js`. This only happens
@@ -23,21 +24,23 @@ export default (Page) => {
// Get the `locale` and `messages` from the request object on the server.
// In the browser, use the same values that the server serialized.
const {req} = context
- const {locale, messages} = req || window.__NEXT_DATA__.props
+ const {locale, messages, antdLocale} = req || window.__NEXT_DATA__.props
// Always update the current time on page load/transition because the
// <IntlProvider> will be a new instance even with pushState routing.
const now = Date.now()
- return {...props, locale, messages, now}
+ return {...props, locale, messages, antdLocale, now}
}
render () {
- const {locale, messages, now, ...props} = this.props
+ const {locale, messages, antdLocale, now, ...props} = this.props
return (
- <IntlProvider locale={locale} messages={messages} initialNow={now}>
- <IntlPage {...props} />
- </IntlProvider>
+ <LocaleProvider locale={ antdLocale }>
+ <IntlProvider locale={locale} messages={messages} initialNow={now}>
+ <IntlPage {...props} />
+ </IntlProvider>
+ </LocaleProvider>
)
}
}
Step5: TypeScript
5.1 Install TypeScript compiler
First install the TypeScript compiler and the plugin for Next.js,
npm install @zeit/next-typescript typescript --save-dev
5.2 Install TypeScript type definitions for libraries
Install all dependented librarys' typings so that vscode can have better code completion for these libraries,
npm install --save-dev @types/next @types/react @types/react-dom @types/react-intl @types/styled-jsx
We don't need to install typings for antd, mobx, react-mobx and mobx-state-tree because they have built-in TypeScript support, i.e., they have their own type definitions.
5.3 Configure TypeScript
5.3.1 tsconfig.json
Create a new file tsconfig.json, and the content of tsconfig.json is as the following:
{
"compileOnSave": false,
"compilerOptions": {
"strict": true,
"target": "esnext",
"module": "esnext",
"jsx": "preserve",
"allowJs": true,
"moduleResolution": "node",
"allowSyntheticDefaultImports": true,
"experimentalDecorators": true,
"noUnusedLocals": true,
"noUnusedParameters": true,
"removeComments": false,
"preserveConstEnums": true,
"sourceMap": true,
"skipLibCheck": true,
"baseUrl": ".",
"typeRoots": [
"./node_modules/@types/",
"./src/@types/"
],
"lib": [
"dom",
"es2015",
"es2016"
]
},
"include": [
"src/**/*"
]
}
In the config above, please pay attention to a few things:
- Use
"jsx": "preserve"instead ofreact-nativebecause Next.js supports.jsxfile extension after version5.0.0, previously it only recogonize.jsfiles. @types/directory must be inside./src, otherwise Typescript won't compile.d.tsfiles in@types/
5.3.2 next.config.js
const withTypescript = require('@zeit/next-typescript')
module.exports = withTypescript({
webpack(config, options) {
return config
}
})
5.3.3 server.js
By default the pages directory is located at the project's root directory, since in this project we move pages to src, we need to tell Next.js where to find pages directory:
const app = next({dev, dir: 'src'})
5.3.4 package.json
Also we need to tell the next build command the location of pages directory in package.json:
"scripts": {
"dev": "node server.js",
"build": "next build ./src && node ./scripts/default-lang.js",
"start": "NODE_ENV=production node server.js"
}
5.3.5 Setting a custom build directory
By default next build will output files to src/.next, which is not clean, I need to move the .next folder out of src.
And we need to tell Next.js to change the output directory to ../build, by default it is src/.next within the same folder that the pages folder resides in. Set distDir in next.config.js:
const withTypescript = require('@zeit/next-typescript')
module.exports = withTypescript({
webpack(config, options) {
return config
},
distDir: '../build'
})
Basically the build process is: ./src -> next build-->./build, then server.js will read files from ./build and run as a server.
5.4 Rewrite all code in Typescript
- Move
./pages/files tosrc/ - Move
./components/files tosrc/ - Move
./containers/files tosrc/ - Rename all
./src/**/*.jsfiles to.tsxfiles, and rewrite them in Typescript - Rename
./stores/ClockStore.jsto./stores/ClockStore.ts
Add a file @types/styled-jsx.d.ts, to make vscode recognize styled-jsx syntax,
import 'react'
// Augmentation of React
declare module 'react' {
interface StyleHTMLAttributes<T> extends React.HTMLAttributes<T> {
jsx?: boolean;
global?: boolean;
}
}
Please read the source code for all details.
5.5 TSLint
5.5.1 Install TSLint
npm install tslint --save-dev
5.5.2 tslint.json
Configure tslint in tslint.json,
{
"extends": ["tslint:latest"],
"rules": {
"semicolon": [true, "never"],
"interface-name": [true, "never-prefix"],
"no-submodule-imports": false,
"no-object-literal-type-assertion": false
}
}
5.5.3 package.json
Add a command to scripts in package.json,
"lint": "tslint \"src/**/*.{ts,tsx}\"",
Now you can run npm run lint to lint your source code.
5.6 prettier
Prettier is a very popular code formatter for Javascript, Typescript and many other frontend languages, first install it,
npm install prettier --save-dev
Add a prettier command to "scripts" section in package.json and remove --fix option of tslint:
"lint": "tslint \"src/**/*.{ts,tsx}\"",
"prettier": "find ./src -type f | xargs prettier --write"
The --write option will modify files in place, so we don't need tslint to fix files.
Configure prettier with prettier.config.js file:
module.exports = {
semi: false,
};
Modify tslint.json to make its rules not conflict with prettier:
{
"extends": ["tslint:latest"],
"rules": {
"arrow-parens": false,
"interface-name": [true, "never-prefix"],
"no-object-literal-type-assertion": false,
"no-submodule-imports": false,
"semicolon": [true, "never"],
"trailing-comma": [true, {"multiline": "nerver", "singleline": "never"}]
}
}
Use pre-commit to run prettier and tslint before commit,
- Install
pre-commitby runningnpm install pre-commit --save-dev - Add a
pre-commitsection topackage.json:"pre-commit": ["prettier", "lint"],
References
- next.js/examples
- Use with React Router 4 · Issue #1632 · zeit/next.js
- Allow custom extensions by nelak · Pull Request #2699 · zeit/next.js
- next.js/examples/with-typescript at master · zeit/next.js
- Revision of with-mobx example by michaelsbradleyjr · Pull Request #3260 · zeit/next.js
Repo liên quan
Master programming by recreating your favorite technologies from scratch.
A curated meta-list of curated lists organized by technology domain. The repository acts as a directory pointing to hundreds of specialized awesome lists covering programming languages, platforms, frameworks, and tooling. All content is community-contributed under the CC0 public domain dedication.
Public APIs is a community-curated GitHub repository listing free, publicly accessible APIs across a wide range of categories, with auth type, HTTPS, and CORS noted for each entry. It's a browsable reference, not a library to install.
freeCodeCamp is a free, self-paced curriculum for learning to code, published as open source at freeCodeCamp/freeCodeCamp. It's a 501(c)(3) nonprofit funded by donor support, structured around six certifications in its Full-Stack Developer Curriculum, each gated by required projects instead of open-book quizzes. The repository also carries beta language certifications for developers, interview-prep resources, and the code that runs the live freecodecamp.org platform.
Trả lời nhanh
Đọc thêm về soulmachine/nextjs-starter-kit ở đâu?
Trang TopGit này là một snapshot — tab "Readme" hiển thị nguyên văn README của repo (đã bỏ link, giữ ảnh). Repo GitHub ở github.com/soulmachine/nextjs-starter-kit là nguồn chính thức.
soulmachine/nextjs-starter-kit có bao nhiêu sao?
soulmachine/nextjs-starter-kit có 105 sao GitHub — tải lại trang để xem số mới nhất, hoặc xem trực tiếp github.com/soulmachine/nextjs-starter-kit. TopGit phản chiếu số sao của GitHub nhưng không cam kết đến từng phút.
soulmachine/nextjs-starter-kit có phải mã nguồn mở không?
Có — soulmachine/nextjs-starter-kit phát hành theo license Apache-2.0, nghĩa là mã nguồn mở để đọc, fork và (tùy license) tái sử dụng. Mã: github.com/soulmachine/nextjs-starter-kit.
soulmachine/nextjs-starter-kit còn đang phát triển không?
Commit gần nhất trên soulmachine/nextjs-starter-kit là 8.7 năm trước (theo timestamp GitHub). Repo có 8 fork — một chỉ báo về mức độ quan tâm của cộng đồng.
soulmachine/nextjs-starter-kit là gì?
soulmachine/nextjs-starter-kit (soulmachine/nextjs-starter-kit) là dự án JavaScript trên GitHub. Theo mô tả gốc: A Next.js + Antd + MobX + Typescript starter kit
Đọc đầy đủ README ở tab phía trên.
Vẫn đang phân vân về nextjs-starter-kit?
Một cú bấm sẽ gửi câu hỏi kèm trang này cho AI — xem AI nói gì về nextjs-starter-kit.