-
Notifications
You must be signed in to change notification settings - Fork 27k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Ability to co-locate non-page files in the pages directory #8454
Comments
The convention is that all files inside Introducing something like Something like this behavior is already possible to achieve by having a separate directory holding this structure and then the public routes would be exposed in |
My team has struggled with this too mostly because our project started out with all components under pages (like what this issue proposes allowing) and, without a recommended path that I've seen, the migration has taken us down a few unproductive roads. At present we have the following three ways to set up pages in our codebase. I know that the Next.js team works hard to study how people use the framework and, have a large Next.js codebase internally. If there are patters that you've found work well, I'd love to hear your thoughts. Write the component in the pages directory
class Customer extends React.Component {
state = {
isLoading: true
};
// Get logged-in-only data
componentDidMount() {
this.setState({ isLoading: true });
this.props.getCustomer(this.props.router.customerId)
.then(() => this.setState({ isLoading: false }));
}
render() {
return (
<main>
<div>{this.props.customer.name}</div>
</main>
);
}
}
const mapStateToProps = state => ({
customer: state.customer || {}
});
const mapDispatchToProps = dispatch => ({
getCustomer: bindActionCreators(customerStore.actions.fetch, dispatch)
});
export default flow(
connect(mapStateToProps, mapDispatchToProps),
withLayout(),
withRouter()
)(Customer); Separate the presentation component from data-fetching, pulling IDs out of routes, and wrapping in layout
const Customer = customer => (
<main>
<div>{customer.name}</div>
</main>
);
export default Customer;
class Customer extends React.Component {
state = {
isLoading: true
};
// Get logged-in-only data
componentDidMount() {
this.setState({ isLoading: true });
this.props.getCustomer(this.props.router.query.customerId)
.then(() => this.setState({ isLoading: false }));
}
render() {
return <Customer customer={this.props.customer} />;
}
}
const mapStateToProps = state => ({
customer: state.customer || {}
});
const mapDispatchToProps = dispatch => ({
getCustomer: bindActionCreators(customerStore.actions.fetch, dispatch)
});
export default flow(
connect(mapStateToProps, mapDispatchToProps),
withLayout()
)(Customer); Only use components in the pages dir for pulling IDs out of routes and wrapping in layout
class Customer extends React.Component {
state = {
isLoading: true
};
// Get logged-in-only data
componentDidMount() {
this.setState({ isLoading: true });
this.props.getCustomer(this.props.id)
.then(() => this.setState({ isLoading: false }));
}
render() {
return (
<div>{this.props.customer.name}</div>
);
}
}
const mapStateToProps = state => ({
customer: state.customer || {}
});
const mapDispatchToProps = dispatch => ({
getCustomer: bindActionCreators(customerStore.actions.fetch, dispatch)
});
export default flow(
connect(mapStateToProps, mapDispatchToProps),
)(Customer);
class Customer extends React.Component {
render() {
return (
<main>
<Customer id={this.props.router.query.customerId} />
</main>
);
}
}
export default flow(
withLayout(),
withRouter()
)(Customer); |
It's extensively discussed in #3728 so I'm thinking we should probably close this issue. @baer for zeit.co we use a combination of the first and second method, we have a |
🤔I'm a bit surprised to hear you're not on the |
We utilize the design system everywhere so we try to avoid introducing page specific components. However if there are page specific components they match the pages folder structure. |
I also use a design system but I'm using Next.js for a dynamic logged-in-only web Application. With the level of complexity for each page, it's impractical to avoid page-specific components. To give you a better sense of what I'm talking about, here's a screenshot of our folder structure.
You can see that I've got three types of files: page entry points in the Is it uncommon to build mostly-dynamic apps like this using Next.js? If no, are there other patterns you've seen out there for keeping the project well structured? Note: Now that project structure drives routing, in addition to compiled pages, and with RFCs like the #8451 and #8063 coming up, directory structure may be an interesting thing to add to #8442. |
So zeit.co is mostly the same except we only have the components directory, so no
It's not uncommon, however I do think that there's currently no "recommended" folder structure in the docs and we might want to provide some guidance there. I'll ask some people running larger apps to reply to this issue (if they want to).
Great point, we didn't want to send the tree in particular as it could be sensitive info. But checking based on heuristics would be fine I think 🙏 |
This is interesting. Our app at The Economist is also structured the way other projects above are:
We actually use the underscore ( Personally I don't hate the idea of having underscore denote files are 'private' but there is probably is value in encouraging people not to fill up |
@probablyup // next.config.js
module.exports = {
pageExtensions: ["page.tsx", "api.ts"],
} a file tree like below, and only
|
To solve this issue, I added getInitialProps in my component and pass a prop to the component. When the component imported in a page Something like:
home.js
mycomp.js
|
@shynome Good idea, but then, instead of |
@GrantGryczan you still can use |
@shynome Adding an additional (edit: second) file for each page you want to import something from elsewhere isn't any more clean than It would be better if something for this were built into Next. My point is that I'm not sure why this issue was closed. Your solution certainly was not sufficient to warrant it. |
@GrantGryczan I think one component one file is a good idea, not all components in one file. |
I see how you misunderstood me and have edited my previous comment. I did not suggest putting all components in one file. That would not even be possible with Next considering each page must be a separate file. I suggested one file per page. What you are suggesting, on the contrary, is one file per page, plus an additional file (two total files) for any page I want to import something from elsewhere. That solution is what I'm saying is no cleaner than |
To be able to use custom Next.js page extensions (vercel/next.js#8454 (comment)), a new property `extensionsRgx` is added to the i18n configuration. # How to use // i18n.js ``` module.exports = { extensionsRgx: /\.(page|api)\.(tsx|ts|js|mjs|jsx)$/, ... } ```
* Add new property `extensionsRgx` to config To be able to use custom Next.js page extensions (vercel/next.js#8454 (comment)), a new property `extensionsRgx` is added to the i18n configuration. # How to use // i18n.js ``` module.exports = { extensionsRgx: /\.(page|api)\.(tsx|ts|js|mjs|jsx)$/, ... } ``` * docs: add `extensionsRgx` configuration property
@GrantGryczan Why would you ever want to import another page elsewhere? Pages are meant to be standalone components. |
* replace quotes only for the special $' pattern specific to .replace() (#529) * replace quotes only for the special $' pattern specific to .replace() instead of replacing quoutes everywhere * use .replace() callback to avoid parsing special string patterns * write tests to verify that templateWithHoc and templateWithLoader correctly replaces special string cases. Update snapshots * Update package version * fix(transCore): when no suffix don't match spaces (#534) * Update package version * _one works (#541) * Update package version * Update README.md (#552) * Update package version * Update dependencies (#554) * Update dependencies * Update example deps * Update Trans text after change lang (#566) * Ignore api.(ts|js...) file (#567) * Add useMemo to useTranslation (#574) * Update deps (#582) * Update version of package.json * Adding tests (#585) * Add new property `extensionsRgx` to config (#589) * Add new property `extensionsRgx` to config To be able to use custom Next.js page extensions (vercel/next.js#8454 (comment)), a new property `extensionsRgx` is added to the i18n configuration. # How to use // i18n.js ``` module.exports = { extensionsRgx: /\.(page|api)\.(tsx|ts|js|mjs|jsx)$/, ... } ``` * docs: add `extensionsRgx` configuration property * Update package.json * Listen for `namespaces` changes and load necessary namespaces (#592) * Update package.json version * Revert "Add useMemo to useTranslation" (#605) This reverts commit 8abc458. # Conflicts: # package.json * Remove console.warn because is already solved on Next.js 10.2.1-canary.4 (#609) Already fixed in [Next.js canary 10.2.1-canary.4](https://github.com/vercel/next.js/releases/tag/v10.2.1-canary.4) Co-authored-by: AndrewB <[email protected]> Co-authored-by: slevy85 <[email protected]> Co-authored-by: Justin <[email protected]> Co-authored-by: Bernd Artmüller <[email protected]> Co-authored-by: Rihards Ščeredins <[email protected]>
There are plenty of use cases: importing a custom error page for use on other pages (which is encouraged in the Next.js docs), importing a TypeScript type from a page (e.g. the type for the page's props), importing something else exported from a page (e.g. a React context, a constant value, etc.) which is impractical to put in a separate module and is relevant specifically only to the page exporting it, and so on. |
…omponents (#22740) Feel free to edit this/suggest changes as you see fit to match the style of your docs or how you want this feature represented. There seemed to be many issues regarding colocating non-page files with page components in the `pages` directory where `pageExtensions` was finally suggested as a solution. I think it would be great to document it better since it seems to be a often requested workflow to colocate test, generated files, styles, and other files needed by page components in the `pages` directory. The note that exists in the docs currently alludes to the ability to do this but it doesn't contain much context that would help make it more discoverable. Relevant issues: #11113 (reply in thread) #8454 (comment) #8617 (comment) #3728 (comment)
Absolutely! The NextJS suggest exactly this on their documentation. |
This issue has been automatically locked due to no recent activity. If you are running into a similar issue, please create a new issue with the steps to reproduce. Thank you. |
Feature request
Is your feature request related to a problem? Please describe.
There are many different methods of codebase organization in the wild and one popular one is to use an
index.js
file for the page component itself and surround it with sibling files for various subviews that are specific to that page.However, next doesn't support this kind of structure because it automatically assumes all files inside of
pages/
are real pages.Describe the solution you'd like
There already is precedent in the codebase for convention over configuration when it comes to filenaming:
[id].js
pattern for dynamic routing_(app|document|error).js
pattern for overriding built-in pagesI propose extending the leading-underscore naming pattern such that any file inside
pages/
prefixed with an underscore will not be considered as a public page and can simply live in the folder.Describe alternatives you've considered
Another prefix I thought of was "$", e.g.
$constants.js
but this felt a little strange to write and actually is not supported in some filesystems.The text was updated successfully, but these errors were encountered: